133e65b0dSMickaël Salaün // SPDX-License-Identifier: GPL-2.0-only 233e65b0dSMickaël Salaün /* 333e65b0dSMickaël Salaün * Landlock - Audit helpers 433e65b0dSMickaël Salaün * 533e65b0dSMickaël Salaün * Copyright © 2023-2025 Microsoft Corporation 633e65b0dSMickaël Salaün */ 733e65b0dSMickaël Salaün 833e65b0dSMickaël Salaün #include <kunit/test.h> 933e65b0dSMickaël Salaün #include <linux/audit.h> 10*2fc80c69SMickaël Salaün #include <linux/bitops.h> 1133e65b0dSMickaël Salaün #include <linux/lsm_audit.h> 121d636984SMickaël Salaün #include <linux/pid.h> 13*2fc80c69SMickaël Salaün #include <uapi/linux/landlock.h> 1433e65b0dSMickaël Salaün 1533e65b0dSMickaël Salaün #include "audit.h" 16*2fc80c69SMickaël Salaün #include "common.h" 1733e65b0dSMickaël Salaün #include "cred.h" 1833e65b0dSMickaël Salaün #include "domain.h" 1933e65b0dSMickaël Salaün #include "limits.h" 2033e65b0dSMickaël Salaün #include "ruleset.h" 2133e65b0dSMickaël Salaün 22*2fc80c69SMickaël Salaün static const char *const fs_access_strings[] = { 23*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = "fs.execute", 24*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = "fs.write_file", 25*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_FILE)] = "fs.read_file", 26*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_DIR)] = "fs.read_dir", 27*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_REMOVE_DIR)] = "fs.remove_dir", 28*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_REMOVE_FILE)] = "fs.remove_file", 29*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_CHAR)] = "fs.make_char", 30*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_DIR)] = "fs.make_dir", 31*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_REG)] = "fs.make_reg", 32*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_SOCK)] = "fs.make_sock", 33*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_FIFO)] = "fs.make_fifo", 34*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_BLOCK)] = "fs.make_block", 35*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_SYM)] = "fs.make_sym", 36*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = "fs.refer", 37*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate", 38*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev", 39*2fc80c69SMickaël Salaün }; 40*2fc80c69SMickaël Salaün 41*2fc80c69SMickaël Salaün static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS); 42*2fc80c69SMickaël Salaün 43*2fc80c69SMickaël Salaün static __attribute_const__ const char * 44*2fc80c69SMickaël Salaün get_blocker(const enum landlock_request_type type, 45*2fc80c69SMickaël Salaün const unsigned long access_bit) 4633e65b0dSMickaël Salaün { 4733e65b0dSMickaël Salaün switch (type) { 4833e65b0dSMickaël Salaün case LANDLOCK_REQUEST_PTRACE: 49*2fc80c69SMickaël Salaün WARN_ON_ONCE(access_bit != -1); 5033e65b0dSMickaël Salaün return "ptrace"; 51c56f6496SMickaël Salaün 52c56f6496SMickaël Salaün case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY: 53*2fc80c69SMickaël Salaün WARN_ON_ONCE(access_bit != -1); 54c56f6496SMickaël Salaün return "fs.change_topology"; 55*2fc80c69SMickaël Salaün 56*2fc80c69SMickaël Salaün case LANDLOCK_REQUEST_FS_ACCESS: 57*2fc80c69SMickaël Salaün if (WARN_ON_ONCE(access_bit >= ARRAY_SIZE(fs_access_strings))) 58*2fc80c69SMickaël Salaün return "unknown"; 59*2fc80c69SMickaël Salaün return fs_access_strings[access_bit]; 6033e65b0dSMickaël Salaün } 6133e65b0dSMickaël Salaün 6233e65b0dSMickaël Salaün WARN_ON_ONCE(1); 6333e65b0dSMickaël Salaün return "unknown"; 6433e65b0dSMickaël Salaün } 6533e65b0dSMickaël Salaün 6633e65b0dSMickaël Salaün static void log_blockers(struct audit_buffer *const ab, 67*2fc80c69SMickaël Salaün const enum landlock_request_type type, 68*2fc80c69SMickaël Salaün const access_mask_t access) 6933e65b0dSMickaël Salaün { 70*2fc80c69SMickaël Salaün const unsigned long access_mask = access; 71*2fc80c69SMickaël Salaün unsigned long access_bit; 72*2fc80c69SMickaël Salaün bool is_first = true; 73*2fc80c69SMickaël Salaün 74*2fc80c69SMickaël Salaün for_each_set_bit(access_bit, &access_mask, BITS_PER_TYPE(access)) { 75*2fc80c69SMickaël Salaün audit_log_format(ab, "%s%s", is_first ? "" : ",", 76*2fc80c69SMickaël Salaün get_blocker(type, access_bit)); 77*2fc80c69SMickaël Salaün is_first = false; 78*2fc80c69SMickaël Salaün } 79*2fc80c69SMickaël Salaün if (is_first) 80*2fc80c69SMickaël Salaün audit_log_format(ab, "%s", get_blocker(type, -1)); 8133e65b0dSMickaël Salaün } 8233e65b0dSMickaël Salaün 831d636984SMickaël Salaün static void log_domain(struct landlock_hierarchy *const hierarchy) 841d636984SMickaël Salaün { 851d636984SMickaël Salaün struct audit_buffer *ab; 861d636984SMickaël Salaün 871d636984SMickaël Salaün /* Ignores already logged domains. */ 881d636984SMickaël Salaün if (READ_ONCE(hierarchy->log_status) == LANDLOCK_LOG_RECORDED) 891d636984SMickaël Salaün return; 901d636984SMickaël Salaün 911d636984SMickaël Salaün /* Uses consistent allocation flags wrt common_lsm_audit(). */ 921d636984SMickaël Salaün ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN, 931d636984SMickaël Salaün AUDIT_LANDLOCK_DOMAIN); 941d636984SMickaël Salaün if (!ab) 951d636984SMickaël Salaün return; 961d636984SMickaël Salaün 971d636984SMickaël Salaün WARN_ON_ONCE(hierarchy->id == 0); 981d636984SMickaël Salaün audit_log_format( 991d636984SMickaël Salaün ab, 1001d636984SMickaël Salaün "domain=%llx status=allocated mode=enforcing pid=%d uid=%u exe=", 1011d636984SMickaël Salaün hierarchy->id, pid_nr(hierarchy->details->pid), 1021d636984SMickaël Salaün hierarchy->details->uid); 1031d636984SMickaël Salaün audit_log_untrustedstring(ab, hierarchy->details->exe_path); 1041d636984SMickaël Salaün audit_log_format(ab, " comm="); 1051d636984SMickaël Salaün audit_log_untrustedstring(ab, hierarchy->details->comm); 1061d636984SMickaël Salaün audit_log_end(ab); 1071d636984SMickaël Salaün 1081d636984SMickaël Salaün /* 1091d636984SMickaël Salaün * There may be race condition leading to logging of the same domain 1101d636984SMickaël Salaün * several times but that is OK. 1111d636984SMickaël Salaün */ 1121d636984SMickaël Salaün WRITE_ONCE(hierarchy->log_status, LANDLOCK_LOG_RECORDED); 1131d636984SMickaël Salaün } 1141d636984SMickaël Salaün 11533e65b0dSMickaël Salaün static struct landlock_hierarchy * 11633e65b0dSMickaël Salaün get_hierarchy(const struct landlock_ruleset *const domain, const size_t layer) 11733e65b0dSMickaël Salaün { 11833e65b0dSMickaël Salaün struct landlock_hierarchy *hierarchy = domain->hierarchy; 11933e65b0dSMickaël Salaün ssize_t i; 12033e65b0dSMickaël Salaün 12133e65b0dSMickaël Salaün if (WARN_ON_ONCE(layer >= domain->num_layers)) 12233e65b0dSMickaël Salaün return hierarchy; 12333e65b0dSMickaël Salaün 12433e65b0dSMickaël Salaün for (i = domain->num_layers - 1; i > layer; i--) { 12533e65b0dSMickaël Salaün if (WARN_ON_ONCE(!hierarchy->parent)) 12633e65b0dSMickaël Salaün break; 12733e65b0dSMickaël Salaün 12833e65b0dSMickaël Salaün hierarchy = hierarchy->parent; 12933e65b0dSMickaël Salaün } 13033e65b0dSMickaël Salaün 13133e65b0dSMickaël Salaün return hierarchy; 13233e65b0dSMickaël Salaün } 13333e65b0dSMickaël Salaün 13433e65b0dSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 13533e65b0dSMickaël Salaün 13633e65b0dSMickaël Salaün static void test_get_hierarchy(struct kunit *const test) 13733e65b0dSMickaël Salaün { 13833e65b0dSMickaël Salaün struct landlock_hierarchy dom0_hierarchy = { 13933e65b0dSMickaël Salaün .id = 10, 14033e65b0dSMickaël Salaün }; 14133e65b0dSMickaël Salaün struct landlock_hierarchy dom1_hierarchy = { 14233e65b0dSMickaël Salaün .parent = &dom0_hierarchy, 14333e65b0dSMickaël Salaün .id = 20, 14433e65b0dSMickaël Salaün }; 14533e65b0dSMickaël Salaün struct landlock_hierarchy dom2_hierarchy = { 14633e65b0dSMickaël Salaün .parent = &dom1_hierarchy, 14733e65b0dSMickaël Salaün .id = 30, 14833e65b0dSMickaël Salaün }; 14933e65b0dSMickaël Salaün struct landlock_ruleset dom2 = { 15033e65b0dSMickaël Salaün .hierarchy = &dom2_hierarchy, 15133e65b0dSMickaël Salaün .num_layers = 3, 15233e65b0dSMickaël Salaün }; 15333e65b0dSMickaël Salaün 15433e65b0dSMickaël Salaün KUNIT_EXPECT_EQ(test, 10, get_hierarchy(&dom2, 0)->id); 15533e65b0dSMickaël Salaün KUNIT_EXPECT_EQ(test, 20, get_hierarchy(&dom2, 1)->id); 15633e65b0dSMickaël Salaün KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, 2)->id); 15733e65b0dSMickaël Salaün KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, -1)->id); 15833e65b0dSMickaël Salaün } 15933e65b0dSMickaël Salaün 16033e65b0dSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 16133e65b0dSMickaël Salaün 162*2fc80c69SMickaël Salaün static size_t get_denied_layer(const struct landlock_ruleset *const domain, 163*2fc80c69SMickaël Salaün access_mask_t *const access_request, 164*2fc80c69SMickaël Salaün const layer_mask_t (*const layer_masks)[], 165*2fc80c69SMickaël Salaün const size_t layer_masks_size) 166*2fc80c69SMickaël Salaün { 167*2fc80c69SMickaël Salaün const unsigned long access_req = *access_request; 168*2fc80c69SMickaël Salaün unsigned long access_bit; 169*2fc80c69SMickaël Salaün access_mask_t missing = 0; 170*2fc80c69SMickaël Salaün long youngest_layer = -1; 171*2fc80c69SMickaël Salaün 172*2fc80c69SMickaël Salaün for_each_set_bit(access_bit, &access_req, layer_masks_size) { 173*2fc80c69SMickaël Salaün const access_mask_t mask = (*layer_masks)[access_bit]; 174*2fc80c69SMickaël Salaün long layer; 175*2fc80c69SMickaël Salaün 176*2fc80c69SMickaël Salaün if (!mask) 177*2fc80c69SMickaël Salaün continue; 178*2fc80c69SMickaël Salaün 179*2fc80c69SMickaël Salaün /* __fls(1) == 0 */ 180*2fc80c69SMickaël Salaün layer = __fls(mask); 181*2fc80c69SMickaël Salaün if (layer > youngest_layer) { 182*2fc80c69SMickaël Salaün youngest_layer = layer; 183*2fc80c69SMickaël Salaün missing = BIT(access_bit); 184*2fc80c69SMickaël Salaün } else if (layer == youngest_layer) { 185*2fc80c69SMickaël Salaün missing |= BIT(access_bit); 186*2fc80c69SMickaël Salaün } 187*2fc80c69SMickaël Salaün } 188*2fc80c69SMickaël Salaün 189*2fc80c69SMickaël Salaün *access_request = missing; 190*2fc80c69SMickaël Salaün if (youngest_layer == -1) 191*2fc80c69SMickaël Salaün return domain->num_layers - 1; 192*2fc80c69SMickaël Salaün 193*2fc80c69SMickaël Salaün return youngest_layer; 194*2fc80c69SMickaël Salaün } 195*2fc80c69SMickaël Salaün 196*2fc80c69SMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 197*2fc80c69SMickaël Salaün 198*2fc80c69SMickaël Salaün static void test_get_denied_layer(struct kunit *const test) 199*2fc80c69SMickaël Salaün { 200*2fc80c69SMickaël Salaün const struct landlock_ruleset dom = { 201*2fc80c69SMickaël Salaün .num_layers = 5, 202*2fc80c69SMickaël Salaün }; 203*2fc80c69SMickaël Salaün const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = { 204*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT(0), 205*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_FILE)] = BIT(1), 206*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_DIR)] = BIT(1) | BIT(0), 207*2fc80c69SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_REMOVE_DIR)] = BIT(2), 208*2fc80c69SMickaël Salaün }; 209*2fc80c69SMickaël Salaün access_mask_t access; 210*2fc80c69SMickaël Salaün 211*2fc80c69SMickaël Salaün access = LANDLOCK_ACCESS_FS_EXECUTE; 212*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 213*2fc80c69SMickaël Salaün get_denied_layer(&dom, &access, &layer_masks, 214*2fc80c69SMickaël Salaün sizeof(layer_masks))); 215*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_EXECUTE); 216*2fc80c69SMickaël Salaün 217*2fc80c69SMickaël Salaün access = LANDLOCK_ACCESS_FS_READ_FILE; 218*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, 1, 219*2fc80c69SMickaël Salaün get_denied_layer(&dom, &access, &layer_masks, 220*2fc80c69SMickaël Salaün sizeof(layer_masks))); 221*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_FILE); 222*2fc80c69SMickaël Salaün 223*2fc80c69SMickaël Salaün access = LANDLOCK_ACCESS_FS_READ_DIR; 224*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, 1, 225*2fc80c69SMickaël Salaün get_denied_layer(&dom, &access, &layer_masks, 226*2fc80c69SMickaël Salaün sizeof(layer_masks))); 227*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR); 228*2fc80c69SMickaël Salaün 229*2fc80c69SMickaël Salaün access = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR; 230*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, 1, 231*2fc80c69SMickaël Salaün get_denied_layer(&dom, &access, &layer_masks, 232*2fc80c69SMickaël Salaün sizeof(layer_masks))); 233*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, access, 234*2fc80c69SMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 235*2fc80c69SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR); 236*2fc80c69SMickaël Salaün 237*2fc80c69SMickaël Salaün access = LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_DIR; 238*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, 1, 239*2fc80c69SMickaël Salaün get_denied_layer(&dom, &access, &layer_masks, 240*2fc80c69SMickaël Salaün sizeof(layer_masks))); 241*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR); 242*2fc80c69SMickaël Salaün 243*2fc80c69SMickaël Salaün access = LANDLOCK_ACCESS_FS_WRITE_FILE; 244*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, 4, 245*2fc80c69SMickaël Salaün get_denied_layer(&dom, &access, &layer_masks, 246*2fc80c69SMickaël Salaün sizeof(layer_masks))); 247*2fc80c69SMickaël Salaün KUNIT_EXPECT_EQ(test, access, 0); 248*2fc80c69SMickaël Salaün } 249*2fc80c69SMickaël Salaün 250*2fc80c69SMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 251*2fc80c69SMickaël Salaün 25233e65b0dSMickaël Salaün static bool is_valid_request(const struct landlock_request *const request) 25333e65b0dSMickaël Salaün { 25433e65b0dSMickaël Salaün if (WARN_ON_ONCE(request->layer_plus_one > LANDLOCK_MAX_NUM_LAYERS)) 25533e65b0dSMickaël Salaün return false; 25633e65b0dSMickaël Salaün 257*2fc80c69SMickaël Salaün if (WARN_ON_ONCE(!(!!request->layer_plus_one ^ !!request->access))) 258*2fc80c69SMickaël Salaün return false; 259*2fc80c69SMickaël Salaün 260*2fc80c69SMickaël Salaün if (request->access) { 261*2fc80c69SMickaël Salaün if (WARN_ON_ONCE(!request->layer_masks)) 262*2fc80c69SMickaël Salaün return false; 263*2fc80c69SMickaël Salaün } else { 264*2fc80c69SMickaël Salaün if (WARN_ON_ONCE(request->layer_masks)) 265*2fc80c69SMickaël Salaün return false; 266*2fc80c69SMickaël Salaün } 267*2fc80c69SMickaël Salaün 268*2fc80c69SMickaël Salaün if (WARN_ON_ONCE(!!request->layer_masks ^ !!request->layer_masks_size)) 26933e65b0dSMickaël Salaün return false; 27033e65b0dSMickaël Salaün 27133e65b0dSMickaël Salaün return true; 27233e65b0dSMickaël Salaün } 27333e65b0dSMickaël Salaün 27433e65b0dSMickaël Salaün /** 27533e65b0dSMickaël Salaün * landlock_log_denial - Create audit records related to a denial 27633e65b0dSMickaël Salaün * 27733e65b0dSMickaël Salaün * @subject: The Landlock subject's credential denying an action. 27833e65b0dSMickaël Salaün * @request: Detail of the user space request. 27933e65b0dSMickaël Salaün */ 28033e65b0dSMickaël Salaün void landlock_log_denial(const struct landlock_cred_security *const subject, 28133e65b0dSMickaël Salaün const struct landlock_request *const request) 28233e65b0dSMickaël Salaün { 28333e65b0dSMickaël Salaün struct audit_buffer *ab; 28433e65b0dSMickaël Salaün struct landlock_hierarchy *youngest_denied; 28533e65b0dSMickaël Salaün size_t youngest_layer; 286*2fc80c69SMickaël Salaün access_mask_t missing; 28733e65b0dSMickaël Salaün 28833e65b0dSMickaël Salaün if (WARN_ON_ONCE(!subject || !subject->domain || 28933e65b0dSMickaël Salaün !subject->domain->hierarchy || !request)) 29033e65b0dSMickaël Salaün return; 29133e65b0dSMickaël Salaün 29233e65b0dSMickaël Salaün if (!is_valid_request(request)) 29333e65b0dSMickaël Salaün return; 29433e65b0dSMickaël Salaün 295*2fc80c69SMickaël Salaün missing = request->access; 296*2fc80c69SMickaël Salaün if (missing) { 297*2fc80c69SMickaël Salaün /* Gets the nearest domain that denies the request. */ 298*2fc80c69SMickaël Salaün if (request->layer_masks) { 299*2fc80c69SMickaël Salaün youngest_layer = get_denied_layer( 300*2fc80c69SMickaël Salaün subject->domain, &missing, request->layer_masks, 301*2fc80c69SMickaël Salaün request->layer_masks_size); 302*2fc80c69SMickaël Salaün } else { 303*2fc80c69SMickaël Salaün /* This will change with the next commit. */ 304*2fc80c69SMickaël Salaün WARN_ON_ONCE(1); 305*2fc80c69SMickaël Salaün youngest_layer = subject->domain->num_layers; 306*2fc80c69SMickaël Salaün } 307*2fc80c69SMickaël Salaün youngest_denied = 308*2fc80c69SMickaël Salaün get_hierarchy(subject->domain, youngest_layer); 309*2fc80c69SMickaël Salaün } else { 31033e65b0dSMickaël Salaün youngest_layer = request->layer_plus_one - 1; 311*2fc80c69SMickaël Salaün youngest_denied = 312*2fc80c69SMickaël Salaün get_hierarchy(subject->domain, youngest_layer); 313*2fc80c69SMickaël Salaün } 31433e65b0dSMickaël Salaün 3151d636984SMickaël Salaün /* 3161d636984SMickaël Salaün * Consistently keeps track of the number of denied access requests 3171d636984SMickaël Salaün * even if audit is currently disabled, or if audit rules currently 3181d636984SMickaël Salaün * exclude this record type, or if landlock_restrict_self(2)'s flags 3191d636984SMickaël Salaün * quiet logs. 3201d636984SMickaël Salaün */ 3211d636984SMickaël Salaün atomic64_inc(&youngest_denied->num_denials); 3221d636984SMickaël Salaün 3231d636984SMickaël Salaün if (!audit_enabled) 3241d636984SMickaël Salaün return; 3251d636984SMickaël Salaün 32633e65b0dSMickaël Salaün /* Ignores denials after an execution. */ 32733e65b0dSMickaël Salaün if (!(subject->domain_exec & (1 << youngest_layer))) 32833e65b0dSMickaël Salaün return; 32933e65b0dSMickaël Salaün 33033e65b0dSMickaël Salaün /* Uses consistent allocation flags wrt common_lsm_audit(). */ 33133e65b0dSMickaël Salaün ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN, 33233e65b0dSMickaël Salaün AUDIT_LANDLOCK_ACCESS); 33333e65b0dSMickaël Salaün if (!ab) 33433e65b0dSMickaël Salaün return; 33533e65b0dSMickaël Salaün 33633e65b0dSMickaël Salaün audit_log_format(ab, "domain=%llx blockers=", youngest_denied->id); 337*2fc80c69SMickaël Salaün log_blockers(ab, request->type, missing); 33833e65b0dSMickaël Salaün audit_log_lsm_data(ab, &request->audit); 33933e65b0dSMickaël Salaün audit_log_end(ab); 3401d636984SMickaël Salaün 3411d636984SMickaël Salaün /* Logs this domain the first time it shows in log. */ 3421d636984SMickaël Salaün log_domain(youngest_denied); 3431d636984SMickaël Salaün } 3441d636984SMickaël Salaün 3451d636984SMickaël Salaün /** 3461d636984SMickaël Salaün * landlock_log_drop_domain - Create an audit record on domain deallocation 3471d636984SMickaël Salaün * 3481d636984SMickaël Salaün * @hierarchy: The domain's hierarchy being deallocated. 3491d636984SMickaël Salaün * 3501d636984SMickaël Salaün * Only domains which previously appeared in the audit logs are logged again. 3511d636984SMickaël Salaün * This is useful to know when a domain will never show again in the audit log. 3521d636984SMickaël Salaün * 3531d636984SMickaël Salaün * Called in a work queue scheduled by landlock_put_ruleset_deferred() called 3541d636984SMickaël Salaün * by hook_cred_free(). 3551d636984SMickaël Salaün */ 3561d636984SMickaël Salaün void landlock_log_drop_domain(const struct landlock_hierarchy *const hierarchy) 3571d636984SMickaël Salaün { 3581d636984SMickaël Salaün struct audit_buffer *ab; 3591d636984SMickaël Salaün 3601d636984SMickaël Salaün if (WARN_ON_ONCE(!hierarchy)) 3611d636984SMickaël Salaün return; 3621d636984SMickaël Salaün 3631d636984SMickaël Salaün if (!audit_enabled) 3641d636984SMickaël Salaün return; 3651d636984SMickaël Salaün 3661d636984SMickaël Salaün /* Ignores domains that were not logged. */ 3671d636984SMickaël Salaün if (READ_ONCE(hierarchy->log_status) != LANDLOCK_LOG_RECORDED) 3681d636984SMickaël Salaün return; 3691d636984SMickaël Salaün 3701d636984SMickaël Salaün /* 3711d636984SMickaël Salaün * If logging of domain allocation succeeded, warns about failure to log 3721d636984SMickaël Salaün * domain deallocation to highlight unbalanced domain lifetime logs. 3731d636984SMickaël Salaün */ 3741d636984SMickaël Salaün ab = audit_log_start(audit_context(), GFP_KERNEL, 3751d636984SMickaël Salaün AUDIT_LANDLOCK_DOMAIN); 3761d636984SMickaël Salaün if (!ab) 3771d636984SMickaël Salaün return; 3781d636984SMickaël Salaün 3791d636984SMickaël Salaün audit_log_format(ab, "domain=%llx status=deallocated denials=%llu", 3801d636984SMickaël Salaün hierarchy->id, atomic64_read(&hierarchy->num_denials)); 3811d636984SMickaël Salaün audit_log_end(ab); 38233e65b0dSMickaël Salaün } 38333e65b0dSMickaël Salaün 38433e65b0dSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 38533e65b0dSMickaël Salaün 38633e65b0dSMickaël Salaün static struct kunit_case test_cases[] = { 38733e65b0dSMickaël Salaün /* clang-format off */ 38833e65b0dSMickaël Salaün KUNIT_CASE(test_get_hierarchy), 389*2fc80c69SMickaël Salaün KUNIT_CASE(test_get_denied_layer), 39033e65b0dSMickaël Salaün {} 39133e65b0dSMickaël Salaün /* clang-format on */ 39233e65b0dSMickaël Salaün }; 39333e65b0dSMickaël Salaün 39433e65b0dSMickaël Salaün static struct kunit_suite test_suite = { 39533e65b0dSMickaël Salaün .name = "landlock_audit", 39633e65b0dSMickaël Salaün .test_cases = test_cases, 39733e65b0dSMickaël Salaün }; 39833e65b0dSMickaël Salaün 39933e65b0dSMickaël Salaün kunit_test_suite(test_suite); 40033e65b0dSMickaël Salaün 40133e65b0dSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 402