1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Landlock - Ptrace and scope hooks
4 *
5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6 * Copyright © 2019-2020 ANSSI
7 * Copyright © 2024-2025 Microsoft Corporation
8 */
9
10 #include <asm/current.h>
11 #include <linux/cleanup.h>
12 #include <linux/cred.h>
13 #include <linux/errno.h>
14 #include <linux/kernel.h>
15 #include <linux/lsm_audit.h>
16 #include <linux/lsm_hooks.h>
17 #include <linux/rcupdate.h>
18 #include <linux/sched.h>
19 #include <linux/sched/signal.h>
20 #include <net/af_unix.h>
21 #include <net/sock.h>
22
23 #include "audit.h"
24 #include "common.h"
25 #include "cred.h"
26 #include "domain.h"
27 #include "fs.h"
28 #include "ruleset.h"
29 #include "setup.h"
30 #include "task.h"
31
32 /**
33 * domain_scope_le - Checks domain ordering for scoped ptrace
34 *
35 * @parent: Parent domain.
36 * @child: Potential child of @parent.
37 *
38 * Checks if the @parent domain is less or equal to (i.e. an ancestor, which
39 * means a subset of) the @child domain.
40 */
domain_scope_le(const struct landlock_ruleset * const parent,const struct landlock_ruleset * const child)41 static bool domain_scope_le(const struct landlock_ruleset *const parent,
42 const struct landlock_ruleset *const child)
43 {
44 const struct landlock_hierarchy *walker;
45
46 /* Quick return for non-landlocked tasks. */
47 if (!parent)
48 return true;
49
50 if (!child)
51 return false;
52
53 for (walker = child->hierarchy; walker; walker = walker->parent) {
54 if (walker == parent->hierarchy)
55 /* @parent is in the scoped hierarchy of @child. */
56 return true;
57 }
58
59 /* There is no relationship between @parent and @child. */
60 return false;
61 }
62
domain_ptrace(const struct landlock_ruleset * const parent,const struct landlock_ruleset * const child)63 static int domain_ptrace(const struct landlock_ruleset *const parent,
64 const struct landlock_ruleset *const child)
65 {
66 if (domain_scope_le(parent, child))
67 return 0;
68
69 return -EPERM;
70 }
71
72 /**
73 * hook_ptrace_access_check - Determines whether the current process may access
74 * another
75 *
76 * @child: Process to be accessed.
77 * @mode: Mode of attachment.
78 *
79 * If the current task has Landlock rules, then the child must have at least
80 * the same rules. Else denied.
81 *
82 * Determines whether a process may access another, returning 0 if permission
83 * granted, -errno if denied.
84 */
hook_ptrace_access_check(struct task_struct * const child,const unsigned int mode)85 static int hook_ptrace_access_check(struct task_struct *const child,
86 const unsigned int mode)
87 {
88 const struct landlock_cred_security *parent_subject;
89 const struct landlock_ruleset *child_dom;
90 int err;
91
92 /* Quick return for non-landlocked tasks. */
93 parent_subject = landlock_cred(current_cred());
94 if (!parent_subject)
95 return 0;
96
97 scoped_guard(rcu)
98 {
99 child_dom = landlock_get_task_domain(child);
100 err = domain_ptrace(parent_subject->domain, child_dom);
101 }
102
103 if (!err)
104 return 0;
105
106 /*
107 * For the ptrace_access_check case, we log the current/parent domain
108 * and the child task.
109 */
110 if (!(mode & PTRACE_MODE_NOAUDIT))
111 landlock_log_denial(parent_subject, &(struct landlock_request) {
112 .type = LANDLOCK_REQUEST_PTRACE,
113 .audit = {
114 .type = LSM_AUDIT_DATA_TASK,
115 .u.tsk = child,
116 },
117 .layer_plus_one = parent_subject->domain->num_layers,
118 });
119
120 return err;
121 }
122
123 /**
124 * hook_ptrace_traceme - Determines whether another process may trace the
125 * current one
126 *
127 * @parent: Task proposed to be the tracer.
128 *
129 * If the parent has Landlock rules, then the current task must have the same
130 * or more rules. Else denied.
131 *
132 * Determines whether the nominated task is permitted to trace the current
133 * process, returning 0 if permission is granted, -errno if denied.
134 */
hook_ptrace_traceme(struct task_struct * const parent)135 static int hook_ptrace_traceme(struct task_struct *const parent)
136 {
137 const struct landlock_cred_security *parent_subject;
138 const struct landlock_ruleset *child_dom;
139 int err;
140
141 child_dom = landlock_get_current_domain();
142
143 guard(rcu)();
144 parent_subject = landlock_cred(__task_cred(parent));
145 err = domain_ptrace(parent_subject->domain, child_dom);
146
147 if (!err)
148 return 0;
149
150 /*
151 * For the ptrace_traceme case, we log the domain which is the cause of
152 * the denial, which means the parent domain instead of the current
153 * domain. This may look unusual because the ptrace_traceme action is a
154 * request to be traced, but the semantic is consistent with
155 * hook_ptrace_access_check().
156 */
157 landlock_log_denial(parent_subject, &(struct landlock_request) {
158 .type = LANDLOCK_REQUEST_PTRACE,
159 .audit = {
160 .type = LSM_AUDIT_DATA_TASK,
161 .u.tsk = current,
162 },
163 .layer_plus_one = parent_subject->domain->num_layers,
164 });
165 return err;
166 }
167
168 /**
169 * domain_is_scoped - Checks if the client domain is scoped in the same
170 * domain as the server.
171 *
172 * @client: IPC sender domain.
173 * @server: IPC receiver domain.
174 * @scope: The scope restriction criteria.
175 *
176 * Returns: True if the @client domain is scoped to access the @server,
177 * unless the @server is also scoped in the same domain as @client.
178 */
domain_is_scoped(const struct landlock_ruleset * const client,const struct landlock_ruleset * const server,access_mask_t scope)179 static bool domain_is_scoped(const struct landlock_ruleset *const client,
180 const struct landlock_ruleset *const server,
181 access_mask_t scope)
182 {
183 int client_layer, server_layer;
184 const struct landlock_hierarchy *client_walker, *server_walker;
185
186 /* Quick return if client has no domain */
187 if (WARN_ON_ONCE(!client))
188 return false;
189
190 client_layer = client->num_layers - 1;
191 client_walker = client->hierarchy;
192 /*
193 * client_layer must be a signed integer with greater capacity
194 * than client->num_layers to ensure the following loop stops.
195 */
196 BUILD_BUG_ON(sizeof(client_layer) > sizeof(client->num_layers));
197
198 server_layer = server ? (server->num_layers - 1) : -1;
199 server_walker = server ? server->hierarchy : NULL;
200
201 /*
202 * Walks client's parent domains down to the same hierarchy level
203 * as the server's domain, and checks that none of these client's
204 * parent domains are scoped.
205 */
206 for (; client_layer > server_layer; client_layer--) {
207 if (landlock_get_scope_mask(client, client_layer) & scope)
208 return true;
209
210 client_walker = client_walker->parent;
211 }
212 /*
213 * Walks server's parent domains down to the same hierarchy level as
214 * the client's domain.
215 */
216 for (; server_layer > client_layer; server_layer--)
217 server_walker = server_walker->parent;
218
219 for (; client_layer >= 0; client_layer--) {
220 if (landlock_get_scope_mask(client, client_layer) & scope) {
221 /*
222 * Client and server are at the same level in the
223 * hierarchy. If the client is scoped, the request is
224 * only allowed if this domain is also a server's
225 * ancestor.
226 */
227 return server_walker != client_walker;
228 }
229 client_walker = client_walker->parent;
230 server_walker = server_walker->parent;
231 }
232 return false;
233 }
234
sock_is_scoped(struct sock * const other,const struct landlock_ruleset * const domain)235 static bool sock_is_scoped(struct sock *const other,
236 const struct landlock_ruleset *const domain)
237 {
238 const struct landlock_ruleset *dom_other;
239
240 /* The credentials will not change. */
241 lockdep_assert_held(&unix_sk(other)->lock);
242 dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain;
243 return domain_is_scoped(domain, dom_other,
244 LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
245 }
246
is_abstract_socket(struct sock * const sock)247 static bool is_abstract_socket(struct sock *const sock)
248 {
249 struct unix_address *addr = unix_sk(sock)->addr;
250
251 if (!addr)
252 return false;
253
254 if (addr->len >= offsetof(struct sockaddr_un, sun_path) + 1 &&
255 addr->name->sun_path[0] == '\0')
256 return true;
257
258 return false;
259 }
260
261 static const struct access_masks unix_scope = {
262 .scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET,
263 };
264
hook_unix_stream_connect(struct sock * const sock,struct sock * const other,struct sock * const newsk)265 static int hook_unix_stream_connect(struct sock *const sock,
266 struct sock *const other,
267 struct sock *const newsk)
268 {
269 size_t handle_layer;
270 const struct landlock_cred_security *const subject =
271 landlock_get_applicable_subject(current_cred(), unix_scope,
272 &handle_layer);
273
274 /* Quick return for non-landlocked tasks. */
275 if (!subject)
276 return 0;
277
278 if (!is_abstract_socket(other))
279 return 0;
280
281 if (!sock_is_scoped(other, subject->domain))
282 return 0;
283
284 landlock_log_denial(subject, &(struct landlock_request) {
285 .type = LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET,
286 .audit = {
287 .type = LSM_AUDIT_DATA_NET,
288 .u.net = &(struct lsm_network_audit) {
289 .sk = other,
290 },
291 },
292 .layer_plus_one = handle_layer + 1,
293 });
294 return -EPERM;
295 }
296
hook_unix_may_send(struct socket * const sock,struct socket * const other)297 static int hook_unix_may_send(struct socket *const sock,
298 struct socket *const other)
299 {
300 size_t handle_layer;
301 const struct landlock_cred_security *const subject =
302 landlock_get_applicable_subject(current_cred(), unix_scope,
303 &handle_layer);
304
305 if (!subject)
306 return 0;
307
308 /*
309 * Checks if this datagram socket was already allowed to be connected
310 * to other.
311 */
312 if (unix_peer(sock->sk) == other->sk)
313 return 0;
314
315 if (!is_abstract_socket(other->sk))
316 return 0;
317
318 if (!sock_is_scoped(other->sk, subject->domain))
319 return 0;
320
321 landlock_log_denial(subject, &(struct landlock_request) {
322 .type = LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET,
323 .audit = {
324 .type = LSM_AUDIT_DATA_NET,
325 .u.net = &(struct lsm_network_audit) {
326 .sk = other->sk,
327 },
328 },
329 .layer_plus_one = handle_layer + 1,
330 });
331 return -EPERM;
332 }
333
334 static const struct access_masks signal_scope = {
335 .scope = LANDLOCK_SCOPE_SIGNAL,
336 };
337
hook_task_kill(struct task_struct * const p,struct kernel_siginfo * const info,const int sig,const struct cred * cred)338 static int hook_task_kill(struct task_struct *const p,
339 struct kernel_siginfo *const info, const int sig,
340 const struct cred *cred)
341 {
342 bool is_scoped;
343 size_t handle_layer;
344 const struct landlock_cred_security *subject;
345
346 if (!cred) {
347 /*
348 * Always allow sending signals between threads of the same process.
349 * This is required for process credential changes by the Native POSIX
350 * Threads Library and implemented by the set*id(2) wrappers and
351 * libcap(3) with tgkill(2). See nptl(7) and libpsx(3).
352 *
353 * This exception is similar to the __ptrace_may_access() one.
354 */
355 if (same_thread_group(p, current))
356 return 0;
357
358 /* Not dealing with USB IO. */
359 cred = current_cred();
360 }
361
362 subject = landlock_get_applicable_subject(cred, signal_scope,
363 &handle_layer);
364
365 /* Quick return for non-landlocked tasks. */
366 if (!subject)
367 return 0;
368
369 scoped_guard(rcu)
370 {
371 is_scoped = domain_is_scoped(subject->domain,
372 landlock_get_task_domain(p),
373 signal_scope.scope);
374 }
375
376 if (!is_scoped)
377 return 0;
378
379 landlock_log_denial(subject, &(struct landlock_request) {
380 .type = LANDLOCK_REQUEST_SCOPE_SIGNAL,
381 .audit = {
382 .type = LSM_AUDIT_DATA_TASK,
383 .u.tsk = p,
384 },
385 .layer_plus_one = handle_layer + 1,
386 });
387 return -EPERM;
388 }
389
hook_file_send_sigiotask(struct task_struct * tsk,struct fown_struct * fown,int signum)390 static int hook_file_send_sigiotask(struct task_struct *tsk,
391 struct fown_struct *fown, int signum)
392 {
393 const struct landlock_cred_security *subject;
394 bool is_scoped = false;
395
396 /* Lock already held by send_sigio() and send_sigurg(). */
397 lockdep_assert_held(&fown->lock);
398 subject = &landlock_file(fown->file)->fown_subject;
399
400 /*
401 * Quick return for unowned socket.
402 *
403 * subject->domain has already been filtered when saved by
404 * hook_file_set_fowner(), so there is no need to call
405 * landlock_get_applicable_subject() here.
406 */
407 if (!subject->domain)
408 return 0;
409
410 scoped_guard(rcu)
411 {
412 is_scoped = domain_is_scoped(subject->domain,
413 landlock_get_task_domain(tsk),
414 signal_scope.scope);
415 }
416
417 if (!is_scoped)
418 return 0;
419
420 landlock_log_denial(subject, &(struct landlock_request) {
421 .type = LANDLOCK_REQUEST_SCOPE_SIGNAL,
422 .audit = {
423 .type = LSM_AUDIT_DATA_TASK,
424 .u.tsk = tsk,
425 },
426 #ifdef CONFIG_AUDIT
427 .layer_plus_one = landlock_file(fown->file)->fown_layer + 1,
428 #endif /* CONFIG_AUDIT */
429 });
430 return -EPERM;
431 }
432
433 static struct security_hook_list landlock_hooks[] __ro_after_init = {
434 LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check),
435 LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
436
437 LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect),
438 LSM_HOOK_INIT(unix_may_send, hook_unix_may_send),
439
440 LSM_HOOK_INIT(task_kill, hook_task_kill),
441 LSM_HOOK_INIT(file_send_sigiotask, hook_file_send_sigiotask),
442 };
443
landlock_add_task_hooks(void)444 __init void landlock_add_task_hooks(void)
445 {
446 security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
447 &landlock_lsmid);
448 }
449