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 "common.h"
24 #include "cred.h"
25 #include "domain.h"
26 #include "fs.h"
27 #include "log.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 *
41 * Return: True if @parent is an ancestor of or equal to @child, false
42 * otherwise.
43 */
domain_scope_le(const struct landlock_domain * const parent,const struct landlock_domain * const child)44 static bool domain_scope_le(const struct landlock_domain *const parent,
45 const struct landlock_domain *const child)
46 {
47 const struct landlock_hierarchy *walker;
48
49 /* Quick return for non-landlocked tasks. */
50 if (!parent)
51 return true;
52
53 if (!child)
54 return false;
55
56 for (walker = child->hierarchy; walker; walker = walker->parent) {
57 if (walker == parent->hierarchy)
58 /* @parent is in the scoped hierarchy of @child. */
59 return true;
60 }
61
62 /* There is no relationship between @parent and @child. */
63 return false;
64 }
65
domain_ptrace(const struct landlock_domain * const parent,const struct landlock_domain * const child)66 static int domain_ptrace(const struct landlock_domain *const parent,
67 const struct landlock_domain *const child)
68 {
69 if (domain_scope_le(parent, child))
70 return 0;
71
72 return -EPERM;
73 }
74
75 /**
76 * hook_ptrace_access_check - Determines whether the current process may access
77 * another
78 *
79 * @child: Process to be accessed.
80 * @mode: Mode of attachment.
81 *
82 * If the current task has Landlock rules, then the child must have at least
83 * the same rules. Else denied.
84 *
85 * Return: 0 if permission is granted, -errno if denied.
86 */
hook_ptrace_access_check(struct task_struct * const child,const unsigned int mode)87 static int hook_ptrace_access_check(struct task_struct *const child,
88 const unsigned int mode)
89 {
90 const struct landlock_cred_security *parent_subject;
91 #ifdef CONFIG_TRACEPOINTS
92 u64 tracee_domain_id = 0;
93 #endif /* CONFIG_TRACEPOINTS */
94 int err;
95
96 /* Quick return for non-landlocked tasks. */
97 parent_subject = landlock_cred(current_cred());
98 if (!parent_subject)
99 return 0;
100
101 scoped_guard(rcu) {
102 const struct landlock_domain *const child_dom =
103 landlock_get_task_domain(child);
104 err = domain_ptrace(parent_subject->domain, child_dom);
105 #ifdef CONFIG_TRACEPOINTS
106 if (child_dom)
107 tracee_domain_id = child_dom->hierarchy->id;
108 #endif /* CONFIG_TRACEPOINTS */
109 }
110
111 if (!err)
112 return 0;
113
114 /*
115 * For the ptrace_access_check case, we log the current/parent domain
116 * and the child task.
117 */
118 if (!(mode & PTRACE_MODE_NOAUDIT))
119 landlock_log_denial(parent_subject, &(struct landlock_request) {
120 .type = LANDLOCK_REQUEST_PTRACE,
121 .audit = {
122 .type = LSM_AUDIT_DATA_TASK,
123 .u.tsk = child,
124 },
125 .layer_plus_one = parent_subject->domain->num_layers,
126 #ifdef CONFIG_TRACEPOINTS
127 .trace_ptrace = &(struct landlock_ptrace_trace) {
128 .tracee_domain_id = tracee_domain_id,
129 .tracer = current,
130 },
131 #endif /* CONFIG_TRACEPOINTS */
132 });
133
134 return err;
135 }
136
137 /**
138 * hook_ptrace_traceme - Determines whether another process may trace the
139 * current one
140 *
141 * @parent: Task proposed to be the tracer.
142 *
143 * If the parent has Landlock rules, then the current task must have the same
144 * or more rules. Else denied.
145 *
146 * Return: 0 if permission is granted, -errno if denied.
147 */
hook_ptrace_traceme(struct task_struct * const parent)148 static int hook_ptrace_traceme(struct task_struct *const parent)
149 {
150 const struct landlock_cred_security *parent_subject;
151 const struct landlock_domain *child_dom;
152 int err;
153
154 child_dom = landlock_get_current_domain();
155
156 guard(rcu)();
157 parent_subject = landlock_cred(__task_cred(parent));
158 err = domain_ptrace(parent_subject->domain, child_dom);
159
160 if (!err)
161 return 0;
162
163 /*
164 * For the ptrace_traceme case, we log the domain which is the cause of
165 * the denial, which means the parent domain instead of the current
166 * domain. This may look unusual because the ptrace_traceme action is a
167 * request to be traced, but the semantic is consistent with
168 * hook_ptrace_access_check().
169 */
170 landlock_log_denial(parent_subject, &(struct landlock_request) {
171 .type = LANDLOCK_REQUEST_PTRACE,
172 .audit = {
173 .type = LSM_AUDIT_DATA_TASK,
174 .u.tsk = current,
175 },
176 .layer_plus_one = parent_subject->domain->num_layers,
177 #ifdef CONFIG_TRACEPOINTS
178 .trace_ptrace = &(struct landlock_ptrace_trace) {
179 /* The current task's domain is stable here. */
180 .tracee_domain_id = child_dom ? child_dom->hierarchy->id : 0,
181 .tracer = parent,
182 },
183 #endif /* CONFIG_TRACEPOINTS */
184 });
185 return err;
186 }
187
188 /**
189 * domain_is_scoped - Check if an interaction from a client/sender to a
190 * server/receiver should be restricted based on scope controls.
191 *
192 * @client: IPC sender domain.
193 * @server: IPC receiver domain.
194 * @scope: The scope restriction criteria.
195 *
196 * Return: True if @server is in a different domain from @client and @client
197 * is scoped to access @server (i.e. access should be denied), false otherwise.
198 */
domain_is_scoped(const struct landlock_domain * const client,const struct landlock_domain * const server,access_mask_t scope)199 static bool domain_is_scoped(const struct landlock_domain *const client,
200 const struct landlock_domain *const server,
201 access_mask_t scope)
202 {
203 int client_layer, server_layer;
204 const struct landlock_hierarchy *client_walker, *server_walker;
205
206 /* Quick return if client has no domain */
207 if (WARN_ON_ONCE(!client))
208 return false;
209
210 client_layer = client->num_layers - 1;
211 client_walker = client->hierarchy;
212 /*
213 * client_layer must be able to represent all numbers from
214 * LANDLOCK_MAX_NUM_LAYERS - 1 to -1 for the loop below to terminate.
215 * (It must be large enough, and it must be signed.)
216 */
217 BUILD_BUG_ON(!is_signed_type(typeof(client_layer)));
218 BUILD_BUG_ON(LANDLOCK_MAX_NUM_LAYERS - 1 >
219 type_max(typeof(client_layer)));
220
221 server_layer = server ? (server->num_layers - 1) : -1;
222 server_walker = server ? server->hierarchy : NULL;
223
224 /*
225 * Walks client's parent domains down to the same hierarchy level
226 * as the server's domain, and checks that none of these client's
227 * parent domains are scoped.
228 */
229 for (; client_layer > server_layer; client_layer--) {
230 if (landlock_get_scope_mask(client, client_layer) & scope)
231 return true;
232
233 client_walker = client_walker->parent;
234 }
235 /*
236 * Walks server's parent domains down to the same hierarchy level as
237 * the client's domain.
238 */
239 for (; server_layer > client_layer; server_layer--)
240 server_walker = server_walker->parent;
241
242 for (; client_layer >= 0; client_layer--) {
243 if (landlock_get_scope_mask(client, client_layer) & scope) {
244 /*
245 * Client and server are at the same level in the
246 * hierarchy. If the client is scoped, the request is
247 * only allowed if this domain is also a server's
248 * ancestor.
249 */
250 return server_walker != client_walker;
251 }
252 client_walker = client_walker->parent;
253 server_walker = server_walker->parent;
254 }
255 return false;
256 }
257
sock_is_scoped(struct sock * const other,const struct landlock_domain * const domain)258 static bool sock_is_scoped(struct sock *const other,
259 const struct landlock_domain *const domain)
260 {
261 const struct landlock_domain *dom_other;
262
263 /* The credentials will not change. */
264 lockdep_assert_held(&unix_sk(other)->lock);
265
266 /*
267 * A live kernel socket (e.g. from sock_create_kern()) has no backing
268 * file, hence no Landlock domain, so treat it as unscoped. The
269 * sk_socket check only guards that dereference; sk_socket is NULL
270 * solely for a dead peer, which the caller already excludes under the
271 * held lock, so no separate SOCK_DEAD check is needed.
272 */
273 if (unlikely(!other->sk_socket || !other->sk_socket->file))
274 return false;
275
276 dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain;
277 return domain_is_scoped(domain, dom_other,
278 LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
279 }
280
281 #ifdef CONFIG_TRACEPOINTS
282
get_socket_domain_id(const struct sock * const other)283 static u64 get_socket_domain_id(const struct sock *const other)
284 {
285 const struct landlock_domain *domain;
286
287 lockdep_assert_held(&unix_sk(other)->lock);
288 domain = landlock_cred(other->sk_socket->file->f_cred)->domain;
289 return domain ? domain->hierarchy->id : 0;
290 }
291
292 #endif /* CONFIG_TRACEPOINTS */
293
is_abstract_socket(struct sock * const sock)294 static bool is_abstract_socket(struct sock *const sock)
295 {
296 struct unix_address *addr = unix_sk(sock)->addr;
297
298 if (!addr)
299 return false;
300
301 if (addr->len >= offsetof(struct sockaddr_un, sun_path) + 1 &&
302 addr->name->sun_path[0] == '\0')
303 return true;
304
305 return false;
306 }
307
308 static const struct access_masks unix_scope = {
309 .scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET,
310 };
311
hook_unix_stream_connect(struct sock * const sock,struct sock * const other,struct sock * const newsk)312 static int hook_unix_stream_connect(struct sock *const sock,
313 struct sock *const other,
314 struct sock *const newsk)
315 {
316 size_t handle_layer;
317 const struct landlock_cred_security *const subject =
318 landlock_get_applicable_subject(current_cred(), unix_scope,
319 &handle_layer);
320
321 /* Quick return for non-landlocked tasks. */
322 if (!subject)
323 return 0;
324
325 if (!is_abstract_socket(other))
326 return 0;
327
328 if (!sock_is_scoped(other, subject->domain))
329 return 0;
330
331 landlock_log_denial(subject, &(struct landlock_request) {
332 .type = LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET,
333 .audit = {
334 .type = LSM_AUDIT_DATA_NET,
335 .u.net = &(struct lsm_network_audit) {
336 .sk = other,
337 },
338 },
339 .layer_plus_one = handle_layer + 1,
340 #ifdef CONFIG_TRACEPOINTS
341 .other_domain_id = get_socket_domain_id(other),
342 #endif /* CONFIG_TRACEPOINTS */
343 });
344 return -EPERM;
345 }
346
hook_unix_may_send(struct socket * const sock,struct socket * const other)347 static int hook_unix_may_send(struct socket *const sock,
348 struct socket *const other)
349 {
350 size_t handle_layer;
351 const struct landlock_cred_security *const subject =
352 landlock_get_applicable_subject(current_cred(), unix_scope,
353 &handle_layer);
354
355 if (!subject)
356 return 0;
357
358 /*
359 * Checks if this datagram socket was already allowed to be connected
360 * to other.
361 */
362 if (unix_peer(sock->sk) == other->sk)
363 return 0;
364
365 if (!is_abstract_socket(other->sk))
366 return 0;
367
368 if (!sock_is_scoped(other->sk, subject->domain))
369 return 0;
370
371 landlock_log_denial(subject, &(struct landlock_request) {
372 .type = LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET,
373 .audit = {
374 .type = LSM_AUDIT_DATA_NET,
375 .u.net = &(struct lsm_network_audit) {
376 .sk = other->sk,
377 },
378 },
379 .layer_plus_one = handle_layer + 1,
380 #ifdef CONFIG_TRACEPOINTS
381 .other_domain_id = get_socket_domain_id(other->sk),
382 #endif /* CONFIG_TRACEPOINTS */
383 });
384 return -EPERM;
385 }
386
387 static const struct access_masks signal_scope = {
388 .scope = LANDLOCK_SCOPE_SIGNAL,
389 };
390
hook_task_kill(struct task_struct * const p,struct kernel_siginfo * const info,const int sig,const struct cred * cred)391 static int hook_task_kill(struct task_struct *const p,
392 struct kernel_siginfo *const info, const int sig,
393 const struct cred *cred)
394 {
395 bool is_scoped;
396 size_t handle_layer;
397 #ifdef CONFIG_TRACEPOINTS
398 u64 target_domain_id = 0;
399 #endif /* CONFIG_TRACEPOINTS */
400 const struct landlock_cred_security *subject;
401
402 if (!cred) {
403 /*
404 * Always allow sending signals between threads of the same process.
405 * This is required for process credential changes by the Native POSIX
406 * Threads Library and implemented by the set*id(2) wrappers and
407 * libcap(3) with tgkill(2). See nptl(7) and libpsx(3).
408 *
409 * This exception is similar to the __ptrace_may_access() one.
410 */
411 if (same_thread_group(p, current))
412 return 0;
413
414 /* Not dealing with USB IO. */
415 cred = current_cred();
416 }
417
418 subject = landlock_get_applicable_subject(cred, signal_scope,
419 &handle_layer);
420
421 /* Quick return for non-landlocked tasks. */
422 if (!subject)
423 return 0;
424
425 scoped_guard(rcu) {
426 const struct landlock_domain *const other =
427 landlock_get_task_domain(p);
428
429 is_scoped = domain_is_scoped(subject->domain, other,
430 signal_scope.scope);
431 #ifdef CONFIG_TRACEPOINTS
432 if (other)
433 target_domain_id = other->hierarchy->id;
434 #endif /* CONFIG_TRACEPOINTS */
435 }
436
437 if (!is_scoped)
438 return 0;
439
440 landlock_log_denial(subject, &(struct landlock_request) {
441 .type = LANDLOCK_REQUEST_SCOPE_SIGNAL,
442 .audit = {
443 .type = LSM_AUDIT_DATA_TASK,
444 .u.tsk = p,
445 },
446 .layer_plus_one = handle_layer + 1,
447 #ifdef CONFIG_TRACEPOINTS
448 .trace_signal = &(struct landlock_signal_trace) {
449 .target_domain_id = target_domain_id,
450 .signal = sig,
451 },
452 #endif /* CONFIG_TRACEPOINTS */
453 });
454 return -EPERM;
455 }
456
hook_file_send_sigiotask(struct task_struct * tsk,struct fown_struct * fown,int signum)457 static int hook_file_send_sigiotask(struct task_struct *tsk,
458 struct fown_struct *fown, int signum)
459 {
460 const struct landlock_cred_security *subject;
461 bool is_scoped = false;
462 #ifdef CONFIG_TRACEPOINTS
463 u64 target_domain_id = 0;
464 #endif /* CONFIG_TRACEPOINTS */
465
466 /* Lock already held by send_sigio() and send_sigurg(). */
467 lockdep_assert_held(&fown->lock);
468 subject = &landlock_file(fown->file)->fown_subject;
469
470 /*
471 * Quick return for unowned socket.
472 *
473 * subject->domain has already been filtered when saved by
474 * hook_file_set_fowner(), so there is no need to call
475 * landlock_get_applicable_subject() here.
476 */
477 if (!subject->domain)
478 return 0;
479
480 /*
481 * Always allow delivery to the file owner's own process, including a
482 * thread-group leader reached through a process-group owner. This
483 * mirrors hook_task_kill()'s same-process exemption and preserves the
484 * guarantee of commit 18eb75f3af40 ("landlock: Always allow signals
485 * between threads of the same process"), which the registration-time
486 * check cannot honor for a process-group target.
487 */
488 if (task_tgid(tsk) == landlock_file(fown->file)->fown_tg)
489 return 0;
490
491 scoped_guard(rcu) {
492 const struct landlock_domain *const other =
493 landlock_get_task_domain(tsk);
494
495 is_scoped = domain_is_scoped(subject->domain, other,
496 signal_scope.scope);
497 #ifdef CONFIG_TRACEPOINTS
498 if (other)
499 target_domain_id = other->hierarchy->id;
500 #endif /* CONFIG_TRACEPOINTS */
501 }
502
503 if (!is_scoped)
504 return 0;
505
506 landlock_log_denial(subject, &(struct landlock_request) {
507 .type = LANDLOCK_REQUEST_SCOPE_SIGNAL,
508 .audit = {
509 .type = LSM_AUDIT_DATA_TASK,
510 .u.tsk = tsk,
511 },
512 #ifdef CONFIG_SECURITY_LANDLOCK_LOG
513 .layer_plus_one = landlock_file(fown->file)->fown_layer + 1,
514 #endif /* CONFIG_SECURITY_LANDLOCK_LOG */
515 #ifdef CONFIG_TRACEPOINTS
516 .trace_signal = &(struct landlock_signal_trace) {
517 .target_domain_id = target_domain_id,
518 .signal = signum ? signum : SIGIO,
519 },
520 #endif /* CONFIG_TRACEPOINTS */
521 });
522 return -EPERM;
523 }
524
525 static struct security_hook_list landlock_hooks[] __ro_after_init = {
526 LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check),
527 LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
528
529 LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect),
530 LSM_HOOK_INIT(unix_may_send, hook_unix_may_send),
531
532 LSM_HOOK_INIT(task_kill, hook_task_kill),
533 LSM_HOOK_INIT(file_send_sigiotask, hook_file_send_sigiotask),
534 };
535
landlock_add_task_hooks(void)536 __init void landlock_add_task_hooks(void)
537 {
538 security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
539 &landlock_lsmid);
540 }
541