xref: /linux/security/landlock/audit.c (revision 33e65b0d3add6bdc731e9298995cbbc979349f51)
1*33e65b0dSMickaël Salaün // SPDX-License-Identifier: GPL-2.0-only
2*33e65b0dSMickaël Salaün /*
3*33e65b0dSMickaël Salaün  * Landlock - Audit helpers
4*33e65b0dSMickaël Salaün  *
5*33e65b0dSMickaël Salaün  * Copyright © 2023-2025 Microsoft Corporation
6*33e65b0dSMickaël Salaün  */
7*33e65b0dSMickaël Salaün 
8*33e65b0dSMickaël Salaün #include <kunit/test.h>
9*33e65b0dSMickaël Salaün #include <linux/audit.h>
10*33e65b0dSMickaël Salaün #include <linux/lsm_audit.h>
11*33e65b0dSMickaël Salaün 
12*33e65b0dSMickaël Salaün #include "audit.h"
13*33e65b0dSMickaël Salaün #include "cred.h"
14*33e65b0dSMickaël Salaün #include "domain.h"
15*33e65b0dSMickaël Salaün #include "limits.h"
16*33e65b0dSMickaël Salaün #include "ruleset.h"
17*33e65b0dSMickaël Salaün 
18*33e65b0dSMickaël Salaün static const char *get_blocker(const enum landlock_request_type type)
19*33e65b0dSMickaël Salaün {
20*33e65b0dSMickaël Salaün 	switch (type) {
21*33e65b0dSMickaël Salaün 	case LANDLOCK_REQUEST_PTRACE:
22*33e65b0dSMickaël Salaün 		return "ptrace";
23*33e65b0dSMickaël Salaün 	}
24*33e65b0dSMickaël Salaün 
25*33e65b0dSMickaël Salaün 	WARN_ON_ONCE(1);
26*33e65b0dSMickaël Salaün 	return "unknown";
27*33e65b0dSMickaël Salaün }
28*33e65b0dSMickaël Salaün 
29*33e65b0dSMickaël Salaün static void log_blockers(struct audit_buffer *const ab,
30*33e65b0dSMickaël Salaün 			 const enum landlock_request_type type)
31*33e65b0dSMickaël Salaün {
32*33e65b0dSMickaël Salaün 	audit_log_format(ab, "%s", get_blocker(type));
33*33e65b0dSMickaël Salaün }
34*33e65b0dSMickaël Salaün 
35*33e65b0dSMickaël Salaün static struct landlock_hierarchy *
36*33e65b0dSMickaël Salaün get_hierarchy(const struct landlock_ruleset *const domain, const size_t layer)
37*33e65b0dSMickaël Salaün {
38*33e65b0dSMickaël Salaün 	struct landlock_hierarchy *hierarchy = domain->hierarchy;
39*33e65b0dSMickaël Salaün 	ssize_t i;
40*33e65b0dSMickaël Salaün 
41*33e65b0dSMickaël Salaün 	if (WARN_ON_ONCE(layer >= domain->num_layers))
42*33e65b0dSMickaël Salaün 		return hierarchy;
43*33e65b0dSMickaël Salaün 
44*33e65b0dSMickaël Salaün 	for (i = domain->num_layers - 1; i > layer; i--) {
45*33e65b0dSMickaël Salaün 		if (WARN_ON_ONCE(!hierarchy->parent))
46*33e65b0dSMickaël Salaün 			break;
47*33e65b0dSMickaël Salaün 
48*33e65b0dSMickaël Salaün 		hierarchy = hierarchy->parent;
49*33e65b0dSMickaël Salaün 	}
50*33e65b0dSMickaël Salaün 
51*33e65b0dSMickaël Salaün 	return hierarchy;
52*33e65b0dSMickaël Salaün }
53*33e65b0dSMickaël Salaün 
54*33e65b0dSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
55*33e65b0dSMickaël Salaün 
56*33e65b0dSMickaël Salaün static void test_get_hierarchy(struct kunit *const test)
57*33e65b0dSMickaël Salaün {
58*33e65b0dSMickaël Salaün 	struct landlock_hierarchy dom0_hierarchy = {
59*33e65b0dSMickaël Salaün 		.id = 10,
60*33e65b0dSMickaël Salaün 	};
61*33e65b0dSMickaël Salaün 	struct landlock_hierarchy dom1_hierarchy = {
62*33e65b0dSMickaël Salaün 		.parent = &dom0_hierarchy,
63*33e65b0dSMickaël Salaün 		.id = 20,
64*33e65b0dSMickaël Salaün 	};
65*33e65b0dSMickaël Salaün 	struct landlock_hierarchy dom2_hierarchy = {
66*33e65b0dSMickaël Salaün 		.parent = &dom1_hierarchy,
67*33e65b0dSMickaël Salaün 		.id = 30,
68*33e65b0dSMickaël Salaün 	};
69*33e65b0dSMickaël Salaün 	struct landlock_ruleset dom2 = {
70*33e65b0dSMickaël Salaün 		.hierarchy = &dom2_hierarchy,
71*33e65b0dSMickaël Salaün 		.num_layers = 3,
72*33e65b0dSMickaël Salaün 	};
73*33e65b0dSMickaël Salaün 
74*33e65b0dSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 10, get_hierarchy(&dom2, 0)->id);
75*33e65b0dSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 20, get_hierarchy(&dom2, 1)->id);
76*33e65b0dSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, 2)->id);
77*33e65b0dSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, -1)->id);
78*33e65b0dSMickaël Salaün }
79*33e65b0dSMickaël Salaün 
80*33e65b0dSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
81*33e65b0dSMickaël Salaün 
82*33e65b0dSMickaël Salaün static bool is_valid_request(const struct landlock_request *const request)
83*33e65b0dSMickaël Salaün {
84*33e65b0dSMickaël Salaün 	if (WARN_ON_ONCE(request->layer_plus_one > LANDLOCK_MAX_NUM_LAYERS))
85*33e65b0dSMickaël Salaün 		return false;
86*33e65b0dSMickaël Salaün 
87*33e65b0dSMickaël Salaün 	if (WARN_ON_ONCE(!request->layer_plus_one))
88*33e65b0dSMickaël Salaün 		return false;
89*33e65b0dSMickaël Salaün 
90*33e65b0dSMickaël Salaün 	return true;
91*33e65b0dSMickaël Salaün }
92*33e65b0dSMickaël Salaün 
93*33e65b0dSMickaël Salaün /**
94*33e65b0dSMickaël Salaün  * landlock_log_denial - Create audit records related to a denial
95*33e65b0dSMickaël Salaün  *
96*33e65b0dSMickaël Salaün  * @subject: The Landlock subject's credential denying an action.
97*33e65b0dSMickaël Salaün  * @request: Detail of the user space request.
98*33e65b0dSMickaël Salaün  */
99*33e65b0dSMickaël Salaün void landlock_log_denial(const struct landlock_cred_security *const subject,
100*33e65b0dSMickaël Salaün 			 const struct landlock_request *const request)
101*33e65b0dSMickaël Salaün {
102*33e65b0dSMickaël Salaün 	struct audit_buffer *ab;
103*33e65b0dSMickaël Salaün 	struct landlock_hierarchy *youngest_denied;
104*33e65b0dSMickaël Salaün 	size_t youngest_layer;
105*33e65b0dSMickaël Salaün 
106*33e65b0dSMickaël Salaün 	if (WARN_ON_ONCE(!subject || !subject->domain ||
107*33e65b0dSMickaël Salaün 			 !subject->domain->hierarchy || !request))
108*33e65b0dSMickaël Salaün 		return;
109*33e65b0dSMickaël Salaün 
110*33e65b0dSMickaël Salaün 	if (!is_valid_request(request))
111*33e65b0dSMickaël Salaün 		return;
112*33e65b0dSMickaël Salaün 
113*33e65b0dSMickaël Salaün 	if (!audit_enabled)
114*33e65b0dSMickaël Salaün 		return;
115*33e65b0dSMickaël Salaün 
116*33e65b0dSMickaël Salaün 	youngest_layer = request->layer_plus_one - 1;
117*33e65b0dSMickaël Salaün 	youngest_denied = get_hierarchy(subject->domain, youngest_layer);
118*33e65b0dSMickaël Salaün 
119*33e65b0dSMickaël Salaün 	/* Ignores denials after an execution. */
120*33e65b0dSMickaël Salaün 	if (!(subject->domain_exec & (1 << youngest_layer)))
121*33e65b0dSMickaël Salaün 		return;
122*33e65b0dSMickaël Salaün 
123*33e65b0dSMickaël Salaün 	/* Uses consistent allocation flags wrt common_lsm_audit(). */
124*33e65b0dSMickaël Salaün 	ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
125*33e65b0dSMickaël Salaün 			     AUDIT_LANDLOCK_ACCESS);
126*33e65b0dSMickaël Salaün 	if (!ab)
127*33e65b0dSMickaël Salaün 		return;
128*33e65b0dSMickaël Salaün 
129*33e65b0dSMickaël Salaün 	audit_log_format(ab, "domain=%llx blockers=", youngest_denied->id);
130*33e65b0dSMickaël Salaün 	log_blockers(ab, request->type);
131*33e65b0dSMickaël Salaün 	audit_log_lsm_data(ab, &request->audit);
132*33e65b0dSMickaël Salaün 	audit_log_end(ab);
133*33e65b0dSMickaël Salaün }
134*33e65b0dSMickaël Salaün 
135*33e65b0dSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
136*33e65b0dSMickaël Salaün 
137*33e65b0dSMickaël Salaün static struct kunit_case test_cases[] = {
138*33e65b0dSMickaël Salaün 	/* clang-format off */
139*33e65b0dSMickaël Salaün 	KUNIT_CASE(test_get_hierarchy),
140*33e65b0dSMickaël Salaün 	{}
141*33e65b0dSMickaël Salaün 	/* clang-format on */
142*33e65b0dSMickaël Salaün };
143*33e65b0dSMickaël Salaün 
144*33e65b0dSMickaël Salaün static struct kunit_suite test_suite = {
145*33e65b0dSMickaël Salaün 	.name = "landlock_audit",
146*33e65b0dSMickaël Salaün 	.test_cases = test_cases,
147*33e65b0dSMickaël Salaün };
148*33e65b0dSMickaël Salaün 
149*33e65b0dSMickaël Salaün kunit_test_suite(test_suite);
150*33e65b0dSMickaël Salaün 
151*33e65b0dSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
152