File Index
Files and directories
Sign in
FILE

task.c

File preview with syntax highlighting.

linux-6.19.14/security/landlock/task.c C
  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 */
 41static 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
 63static 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 */
 85static 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	int err;
 90
 91	/* Quick return for non-landlocked tasks. */
 92	parent_subject = landlock_cred(current_cred());
 93	if (!parent_subject)
 94		return 0;
 95
 96	scoped_guard(rcu)
 97	{
 98		const struct landlock_ruleset *const child_dom =
 99			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 */
135static 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 - Check if an interaction from a client/sender to a
170 *		      server/receiver should be restricted based on scope controls.
171 *
172 * @client: IPC sender domain.
173 * @server: IPC receiver domain.
174 * @scope: The scope restriction criteria.
175 *
176 * Returns: True if @server is in a different domain from @client, and @client
177 * is scoped to access @server (i.e. access should be denied).
178 */
179static 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
235static 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
247static 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
261static const struct access_masks unix_scope = {
262	.scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET,
263};
264
265static 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
297static 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
334static const struct access_masks signal_scope = {
335	.scope = LANDLOCK_SCOPE_SIGNAL,
336};
337
338static 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
390static 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
433static 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
444__init void landlock_add_task_hooks(void)
445{
446	security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
447			   &landlock_lsmid);
448}
~/ Read-only file preview.