projects/linux·blob master · refs

237 lines · 6.7 KiB

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
31static const char *const fs_access_strings[] = { _LANDLOCK_ACCESS_FS_NAMES };
32
33static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);
34
35static const char *const net_access_strings[] = { _LANDLOCK_ACCESS_NET_NAMES };
36
37static_assert(ARRAY_SIZE(net_access_strings) == LANDLOCK_NUM_ACCESS_NET);
38
39static const char *const scope_strings[] = { _LANDLOCK_SCOPE_NAMES };
40
41static_assert(ARRAY_SIZE(scope_strings) == LANDLOCK_NUM_SCOPE);
42
43#undef _LANDLOCK_NAME_ENTRY
44
45static __attribute_const__ const char *
46get_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 */
88static __attribute_const__ const char *
89blocker_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
111static 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
129static 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 */
173void 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 */
214void 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}