xref: /openbmc/linux/security/landlock/ptrace.c (revision f22f9aaf)
1afe81f75SMickaël Salaün // SPDX-License-Identifier: GPL-2.0-only
2afe81f75SMickaël Salaün /*
3afe81f75SMickaël Salaün  * Landlock LSM - Ptrace hooks
4afe81f75SMickaël Salaün  *
5afe81f75SMickaël Salaün  * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6afe81f75SMickaël Salaün  * Copyright © 2019-2020 ANSSI
7afe81f75SMickaël Salaün  */
8afe81f75SMickaël Salaün 
9afe81f75SMickaël Salaün #include <asm/current.h>
10afe81f75SMickaël Salaün #include <linux/cred.h>
11afe81f75SMickaël Salaün #include <linux/errno.h>
12afe81f75SMickaël Salaün #include <linux/kernel.h>
13afe81f75SMickaël Salaün #include <linux/lsm_hooks.h>
14afe81f75SMickaël Salaün #include <linux/rcupdate.h>
15afe81f75SMickaël Salaün #include <linux/sched.h>
16afe81f75SMickaël Salaün 
17afe81f75SMickaël Salaün #include "common.h"
18afe81f75SMickaël Salaün #include "cred.h"
19afe81f75SMickaël Salaün #include "ptrace.h"
20afe81f75SMickaël Salaün #include "ruleset.h"
21afe81f75SMickaël Salaün #include "setup.h"
22afe81f75SMickaël Salaün 
23afe81f75SMickaël Salaün /**
24afe81f75SMickaël Salaün  * domain_scope_le - Checks domain ordering for scoped ptrace
25afe81f75SMickaël Salaün  *
26afe81f75SMickaël Salaün  * @parent: Parent domain.
27afe81f75SMickaël Salaün  * @child: Potential child of @parent.
28afe81f75SMickaël Salaün  *
29afe81f75SMickaël Salaün  * Checks if the @parent domain is less or equal to (i.e. an ancestor, which
30afe81f75SMickaël Salaün  * means a subset of) the @child domain.
31afe81f75SMickaël Salaün  */
domain_scope_le(const struct landlock_ruleset * const parent,const struct landlock_ruleset * const child)32afe81f75SMickaël Salaün static bool domain_scope_le(const struct landlock_ruleset *const parent,
33afe81f75SMickaël Salaün 			    const struct landlock_ruleset *const child)
34afe81f75SMickaël Salaün {
35afe81f75SMickaël Salaün 	const struct landlock_hierarchy *walker;
36afe81f75SMickaël Salaün 
37afe81f75SMickaël Salaün 	if (!parent)
38afe81f75SMickaël Salaün 		return true;
39afe81f75SMickaël Salaün 	if (!child)
40afe81f75SMickaël Salaün 		return false;
41afe81f75SMickaël Salaün 	for (walker = child->hierarchy; walker; walker = walker->parent) {
42afe81f75SMickaël Salaün 		if (walker == parent->hierarchy)
43afe81f75SMickaël Salaün 			/* @parent is in the scoped hierarchy of @child. */
44afe81f75SMickaël Salaün 			return true;
45afe81f75SMickaël Salaün 	}
46afe81f75SMickaël Salaün 	/* There is no relationship between @parent and @child. */
47afe81f75SMickaël Salaün 	return false;
48afe81f75SMickaël Salaün }
49afe81f75SMickaël Salaün 
task_is_scoped(const struct task_struct * const parent,const struct task_struct * const child)50afe81f75SMickaël Salaün static bool task_is_scoped(const struct task_struct *const parent,
51afe81f75SMickaël Salaün 			   const struct task_struct *const child)
52afe81f75SMickaël Salaün {
53afe81f75SMickaël Salaün 	bool is_scoped;
54afe81f75SMickaël Salaün 	const struct landlock_ruleset *dom_parent, *dom_child;
55afe81f75SMickaël Salaün 
56afe81f75SMickaël Salaün 	rcu_read_lock();
57afe81f75SMickaël Salaün 	dom_parent = landlock_get_task_domain(parent);
58afe81f75SMickaël Salaün 	dom_child = landlock_get_task_domain(child);
59afe81f75SMickaël Salaün 	is_scoped = domain_scope_le(dom_parent, dom_child);
60afe81f75SMickaël Salaün 	rcu_read_unlock();
61afe81f75SMickaël Salaün 	return is_scoped;
62afe81f75SMickaël Salaün }
63afe81f75SMickaël Salaün 
task_ptrace(const struct task_struct * const parent,const struct task_struct * const child)64afe81f75SMickaël Salaün static int task_ptrace(const struct task_struct *const parent,
65afe81f75SMickaël Salaün 		       const struct task_struct *const child)
66afe81f75SMickaël Salaün {
67afe81f75SMickaël Salaün 	/* Quick return for non-landlocked tasks. */
68afe81f75SMickaël Salaün 	if (!landlocked(parent))
69afe81f75SMickaël Salaün 		return 0;
70afe81f75SMickaël Salaün 	if (task_is_scoped(parent, child))
71afe81f75SMickaël Salaün 		return 0;
72afe81f75SMickaël Salaün 	return -EPERM;
73afe81f75SMickaël Salaün }
74afe81f75SMickaël Salaün 
75afe81f75SMickaël Salaün /**
76afe81f75SMickaël Salaün  * hook_ptrace_access_check - Determines whether the current process may access
77afe81f75SMickaël Salaün  *			      another
78afe81f75SMickaël Salaün  *
79afe81f75SMickaël Salaün  * @child: Process to be accessed.
80afe81f75SMickaël Salaün  * @mode: Mode of attachment.
81afe81f75SMickaël Salaün  *
82afe81f75SMickaël Salaün  * If the current task has Landlock rules, then the child must have at least
83afe81f75SMickaël Salaün  * the same rules.  Else denied.
84afe81f75SMickaël Salaün  *
85afe81f75SMickaël Salaün  * Determines whether a process may access another, returning 0 if permission
86afe81f75SMickaël Salaün  * granted, -errno if denied.
87afe81f75SMickaël Salaün  */
hook_ptrace_access_check(struct task_struct * const child,const unsigned int mode)88afe81f75SMickaël Salaün static int hook_ptrace_access_check(struct task_struct *const child,
89afe81f75SMickaël Salaün 				    const unsigned int mode)
90afe81f75SMickaël Salaün {
91afe81f75SMickaël Salaün 	return task_ptrace(current, child);
92afe81f75SMickaël Salaün }
93afe81f75SMickaël Salaün 
94afe81f75SMickaël Salaün /**
95afe81f75SMickaël Salaün  * hook_ptrace_traceme - Determines whether another process may trace the
96afe81f75SMickaël Salaün  *			 current one
97afe81f75SMickaël Salaün  *
98afe81f75SMickaël Salaün  * @parent: Task proposed to be the tracer.
99afe81f75SMickaël Salaün  *
100afe81f75SMickaël Salaün  * If the parent has Landlock rules, then the current task must have the same
101afe81f75SMickaël Salaün  * or more rules.  Else denied.
102afe81f75SMickaël Salaün  *
103afe81f75SMickaël Salaün  * Determines whether the nominated task is permitted to trace the current
104afe81f75SMickaël Salaün  * process, returning 0 if permission is granted, -errno if denied.
105afe81f75SMickaël Salaün  */
hook_ptrace_traceme(struct task_struct * const parent)106afe81f75SMickaël Salaün static int hook_ptrace_traceme(struct task_struct *const parent)
107afe81f75SMickaël Salaün {
108afe81f75SMickaël Salaün 	return task_ptrace(parent, current);
109afe81f75SMickaël Salaün }
110afe81f75SMickaël Salaün 
111*f22f9aafSPaul Moore static struct security_hook_list landlock_hooks[] __ro_after_init = {
112afe81f75SMickaël Salaün 	LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check),
113afe81f75SMickaël Salaün 	LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
114afe81f75SMickaël Salaün };
115afe81f75SMickaël Salaün 
landlock_add_ptrace_hooks(void)116afe81f75SMickaël Salaün __init void landlock_add_ptrace_hooks(void)
117afe81f75SMickaël Salaün {
118afe81f75SMickaël Salaün 	security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
119afe81f75SMickaël Salaün 			   LANDLOCK_NAME);
120afe81f75SMickaël Salaün }
121