xref: /linux/security/landlock/audit.c (revision 83684c4e4d62cb02b2e4d0d18963d1035439278e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Landlock - Audit helpers
4  *
5  * Copyright © 2023-2025 Microsoft Corporation
6  */
7 
8 #include <linux/audit.h>
9 #include <linux/bitops.h>
10 #include <linux/landlock.h>
11 #include <linux/lsm_audit.h>
12 #include <linux/pid.h>
13 #include <uapi/linux/landlock.h>
14 
15 #include "access.h"
16 #include "audit.h"
17 #include "common.h"
18 #include "cred.h"
19 #include "domain.h"
20 #include "limits.h"
21 #include "log.h"
22 
23 /*
24  * Access-right and scope names are built from the lists shared with the trace
25  * events (see <linux/landlock.h>).  The designated initializer places each name
26  * at its bit index, so the lookup stays O(1) and does not depend on the entry
27  * order.  log_blockers() adds the "fs."/"net."/"scope." category prefix.
28  */
29 #define _LANDLOCK_NAME_ENTRY(mask, name) [BIT_INDEX(mask)] = name
30 
31 static const char *const fs_access_strings[] = { _LANDLOCK_ACCESS_FS_NAMES };
32 
33 static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);
34 
35 static const char *const net_access_strings[] = { _LANDLOCK_ACCESS_NET_NAMES };
36 
37 static_assert(ARRAY_SIZE(net_access_strings) == LANDLOCK_NUM_ACCESS_NET);
38 
39 static const char *const scope_strings[] = { _LANDLOCK_SCOPE_NAMES };
40 
41 static_assert(ARRAY_SIZE(scope_strings) == LANDLOCK_NUM_SCOPE);
42 
43 #undef _LANDLOCK_NAME_ENTRY
44 
45 static __attribute_const__ const char *
46 get_blocker(const enum landlock_request_type type,
47 	    const unsigned long access_bit)
48 {
49 	switch (type) {
50 	case LANDLOCK_REQUEST_PTRACE:
51 		WARN_ON_ONCE(access_bit != -1);
52 		return "ptrace";
53 
54 	case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY:
55 		WARN_ON_ONCE(access_bit != -1);
56 		return "change_topology";
57 
58 	case LANDLOCK_REQUEST_FS_ACCESS:
59 		if (WARN_ON_ONCE(access_bit >= ARRAY_SIZE(fs_access_strings)))
60 			return "unknown";
61 		return fs_access_strings[access_bit];
62 
63 	case LANDLOCK_REQUEST_NET_ACCESS:
64 		if (WARN_ON_ONCE(access_bit >= ARRAY_SIZE(net_access_strings)))
65 			return "unknown";
66 		return net_access_strings[access_bit];
67 
68 	case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
69 		WARN_ON_ONCE(access_bit != -1);
70 		return scope_strings[BIT_INDEX(
71 			LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET)];
72 
73 	case LANDLOCK_REQUEST_SCOPE_SIGNAL:
74 		WARN_ON_ONCE(access_bit != -1);
75 		return scope_strings[BIT_INDEX(LANDLOCK_SCOPE_SIGNAL)];
76 	}
77 
78 	WARN_ON_ONCE(1);
79 	return "unknown";
80 }
81 
82 /*
83  * Returns the audit category prefix prepended to the unprefixed blocker name
84  * returned by get_blocker() (filesystem and network access rights,
85  * change_topology, and scopes).  The ptrace blocker is standalone and carries
86  * its full name in get_blocker(), so it uses no prefix.
87  */
88 static __attribute_const__ const char *
89 blocker_prefix(const enum landlock_request_type type)
90 {
91 	switch (type) {
92 	case LANDLOCK_REQUEST_PTRACE:
93 		return "";
94 
95 	case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY:
96 	case LANDLOCK_REQUEST_FS_ACCESS:
97 		return "fs.";
98 
99 	case LANDLOCK_REQUEST_NET_ACCESS:
100 		return "net.";
101 
102 	case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
103 	case LANDLOCK_REQUEST_SCOPE_SIGNAL:
104 		return "scope.";
105 	}
106 
107 	WARN_ON_ONCE(1);
108 	return "";
109 }
110 
111 static void log_blockers(struct audit_buffer *const ab,
112 			 const enum landlock_request_type type,
113 			 const access_mask_t access)
114 {
115 	const unsigned long access_mask = access;
116 	const char *const prefix = blocker_prefix(type);
117 	unsigned long access_bit;
118 	bool is_first = true;
119 
120 	for_each_set_bit(access_bit, &access_mask, BITS_PER_TYPE(access)) {
121 		audit_log_format(ab, "%s%s%s", is_first ? "" : ",", prefix,
122 				 get_blocker(type, access_bit));
123 		is_first = false;
124 	}
125 	if (is_first)
126 		audit_log_format(ab, "%s%s", prefix, get_blocker(type, -1));
127 }
128 
129 static void log_domain(struct landlock_hierarchy *const hierarchy)
130 {
131 	struct audit_buffer *ab;
132 
133 	/* Ignores already logged domains.  */
134 	if (READ_ONCE(hierarchy->log_status) == LANDLOCK_LOG_RECORDED)
135 		return;
136 
137 	/* Uses consistent allocation flags wrt common_lsm_audit(). */
138 	ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
139 			     AUDIT_LANDLOCK_DOMAIN);
140 	if (!ab)
141 		return;
142 
143 	WARN_ON_ONCE(hierarchy->id == 0);
144 	audit_log_format(
145 		ab,
146 		"domain=%llx status=allocated mode=enforcing pid=%d uid=%u exe=",
147 		hierarchy->id, pid_nr(hierarchy->details->pid),
148 		hierarchy->details->uid);
149 	audit_log_untrustedstring(ab, hierarchy->details->exe_path);
150 	audit_log_format(ab, " comm=");
151 	audit_log_untrustedstring(ab, hierarchy->details->comm);
152 	audit_log_end(ab);
153 
154 	/*
155 	 * There may be race condition leading to logging of the same domain
156 	 * several times but that is OK.
157 	 */
158 	WRITE_ONCE(hierarchy->log_status, LANDLOCK_LOG_RECORDED);
159 }
160 
161 /**
162  * landlock_audit_denial - Create an audit record for a denied access request
163  *
164  * @request: Detail of the user space request.
165  * @youngest_denied: The youngest hierarchy node that denied the access.
166  * @missing: The set of denied access rights.
167  * @logged: Whether the denial is selected for logging, as computed by
168  *          landlock_log_denial() (domain policy and quiet rules).
169  *
170  * Emits the record when audit is enabled and the denial is selected for
171  * logging.
172  */
173 void landlock_audit_denial(const struct landlock_request *const request,
174 			   struct landlock_hierarchy *const youngest_denied,
175 			   const access_mask_t missing, const bool logged)
176 {
177 	struct audit_buffer *ab;
178 
179 	if (!audit_enabled)
180 		return;
181 
182 	/*
183 	 * Skips denials the domain's policy or a quiet rule excludes from
184 	 * logging (folded into @logged by landlock_log_denial()).
185 	 */
186 	if (!logged)
187 		return;
188 
189 	/* Uses consistent allocation flags wrt common_lsm_audit(). */
190 	ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
191 			     AUDIT_LANDLOCK_ACCESS);
192 	if (!ab)
193 		return;
194 
195 	audit_log_format(ab, "domain=%llx blockers=", youngest_denied->id);
196 	log_blockers(ab, request->type, missing);
197 	audit_log_lsm_data(ab, &request->audit);
198 	audit_log_end(ab);
199 
200 	/* Logs this domain the first time it shows in log. */
201 	log_domain(youngest_denied);
202 }
203 
204 /**
205  * landlock_audit_free_domain - Create an audit record on domain deallocation
206  *
207  * @hierarchy: The domain's hierarchy being deallocated.
208  *
209  * Only domains which previously appeared in the audit logs are logged again.
210  * This is useful to know when a domain will never show again in the audit log.
211  *
212  * Called from landlock_log_free_domain().
213  */
214 void landlock_audit_free_domain(const struct landlock_hierarchy *const hierarchy)
215 {
216 	struct audit_buffer *ab;
217 
218 	if (!audit_enabled)
219 		return;
220 
221 	/* Ignores domains that were not logged.  */
222 	if (READ_ONCE(hierarchy->log_status) != LANDLOCK_LOG_RECORDED)
223 		return;
224 
225 	/*
226 	 * If logging of domain allocation succeeded, warns about failure to log
227 	 * domain deallocation to highlight unbalanced domain lifetime logs.
228 	 */
229 	ab = audit_log_start(audit_context(), GFP_KERNEL,
230 			     AUDIT_LANDLOCK_DOMAIN);
231 	if (!ab)
232 		return;
233 
234 	audit_log_format(ab, "domain=%llx status=deallocated denials=%llu",
235 			 hierarchy->id, atomic64_read(&hierarchy->num_denials));
236 	audit_log_end(ab);
237 }
238