xref: /linux/security/landlock/task.c (revision 0889db596a25ecde210e66fa0db70bc6a6d91f6c)
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  */
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 
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  */
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  */
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  */
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 
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 
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 
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 
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 
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 
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 
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 
536 __init void landlock_add_task_hooks(void)
537 {
538 	security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
539 			   &landlock_lsmid);
540 }
541