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