xref: /linux/security/landlock/task.c (revision 369b48b43a09f995876bb2e88d78845eb2a80212)
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 #include <net/af_unix.h>
17 #include <net/sock.h>
18 
19 #include "common.h"
20 #include "cred.h"
21 #include "ruleset.h"
22 #include "setup.h"
23 #include "task.h"
24 
25 /**
26  * domain_scope_le - Checks domain ordering for scoped ptrace
27  *
28  * @parent: Parent domain.
29  * @child: Potential child of @parent.
30  *
31  * Checks if the @parent domain is less or equal to (i.e. an ancestor, which
32  * means a subset of) the @child domain.
33  */
34 static bool domain_scope_le(const struct landlock_ruleset *const parent,
35 			    const struct landlock_ruleset *const child)
36 {
37 	const struct landlock_hierarchy *walker;
38 
39 	if (!parent)
40 		return true;
41 	if (!child)
42 		return false;
43 	for (walker = child->hierarchy; walker; walker = walker->parent) {
44 		if (walker == parent->hierarchy)
45 			/* @parent is in the scoped hierarchy of @child. */
46 			return true;
47 	}
48 	/* There is no relationship between @parent and @child. */
49 	return false;
50 }
51 
52 static bool task_is_scoped(const struct task_struct *const parent,
53 			   const struct task_struct *const child)
54 {
55 	bool is_scoped;
56 	const struct landlock_ruleset *dom_parent, *dom_child;
57 
58 	rcu_read_lock();
59 	dom_parent = landlock_get_task_domain(parent);
60 	dom_child = landlock_get_task_domain(child);
61 	is_scoped = domain_scope_le(dom_parent, dom_child);
62 	rcu_read_unlock();
63 	return is_scoped;
64 }
65 
66 static int task_ptrace(const struct task_struct *const parent,
67 		       const struct task_struct *const child)
68 {
69 	/* Quick return for non-landlocked tasks. */
70 	if (!landlocked(parent))
71 		return 0;
72 	if (task_is_scoped(parent, child))
73 		return 0;
74 	return -EPERM;
75 }
76 
77 /**
78  * hook_ptrace_access_check - Determines whether the current process may access
79  *			      another
80  *
81  * @child: Process to be accessed.
82  * @mode: Mode of attachment.
83  *
84  * If the current task has Landlock rules, then the child must have at least
85  * the same rules.  Else denied.
86  *
87  * Determines whether a process may access another, returning 0 if permission
88  * granted, -errno if denied.
89  */
90 static int hook_ptrace_access_check(struct task_struct *const child,
91 				    const unsigned int mode)
92 {
93 	return task_ptrace(current, child);
94 }
95 
96 /**
97  * hook_ptrace_traceme - Determines whether another process may trace the
98  *			 current one
99  *
100  * @parent: Task proposed to be the tracer.
101  *
102  * If the parent has Landlock rules, then the current task must have the same
103  * or more rules.  Else denied.
104  *
105  * Determines whether the nominated task is permitted to trace the current
106  * process, returning 0 if permission is granted, -errno if denied.
107  */
108 static int hook_ptrace_traceme(struct task_struct *const parent)
109 {
110 	return task_ptrace(parent, current);
111 }
112 
113 /**
114  * domain_is_scoped - Checks if the client domain is scoped in the same
115  *		      domain as the server.
116  *
117  * @client: IPC sender domain.
118  * @server: IPC receiver domain.
119  * @scope: The scope restriction criteria.
120  *
121  * Returns: True if the @client domain is scoped to access the @server,
122  * unless the @server is also scoped in the same domain as @client.
123  */
124 static bool domain_is_scoped(const struct landlock_ruleset *const client,
125 			     const struct landlock_ruleset *const server,
126 			     access_mask_t scope)
127 {
128 	int client_layer, server_layer;
129 	struct landlock_hierarchy *client_walker, *server_walker;
130 
131 	/* Quick return if client has no domain */
132 	if (WARN_ON_ONCE(!client))
133 		return false;
134 
135 	client_layer = client->num_layers - 1;
136 	client_walker = client->hierarchy;
137 	/*
138 	 * client_layer must be a signed integer with greater capacity
139 	 * than client->num_layers to ensure the following loop stops.
140 	 */
141 	BUILD_BUG_ON(sizeof(client_layer) > sizeof(client->num_layers));
142 
143 	server_layer = server ? (server->num_layers - 1) : -1;
144 	server_walker = server ? server->hierarchy : NULL;
145 
146 	/*
147 	 * Walks client's parent domains down to the same hierarchy level
148 	 * as the server's domain, and checks that none of these client's
149 	 * parent domains are scoped.
150 	 */
151 	for (; client_layer > server_layer; client_layer--) {
152 		if (landlock_get_scope_mask(client, client_layer) & scope)
153 			return true;
154 
155 		client_walker = client_walker->parent;
156 	}
157 	/*
158 	 * Walks server's parent domains down to the same hierarchy level as
159 	 * the client's domain.
160 	 */
161 	for (; server_layer > client_layer; server_layer--)
162 		server_walker = server_walker->parent;
163 
164 	for (; client_layer >= 0; client_layer--) {
165 		if (landlock_get_scope_mask(client, client_layer) & scope) {
166 			/*
167 			 * Client and server are at the same level in the
168 			 * hierarchy. If the client is scoped, the request is
169 			 * only allowed if this domain is also a server's
170 			 * ancestor.
171 			 */
172 			return server_walker != client_walker;
173 		}
174 		client_walker = client_walker->parent;
175 		server_walker = server_walker->parent;
176 	}
177 	return false;
178 }
179 
180 static bool sock_is_scoped(struct sock *const other,
181 			   const struct landlock_ruleset *const domain)
182 {
183 	const struct landlock_ruleset *dom_other;
184 
185 	/* The credentials will not change. */
186 	lockdep_assert_held(&unix_sk(other)->lock);
187 	dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain;
188 	return domain_is_scoped(domain, dom_other,
189 				LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
190 }
191 
192 static bool is_abstract_socket(struct sock *const sock)
193 {
194 	struct unix_address *addr = unix_sk(sock)->addr;
195 
196 	if (!addr)
197 		return false;
198 
199 	if (addr->len >= offsetof(struct sockaddr_un, sun_path) + 1 &&
200 	    addr->name->sun_path[0] == '\0')
201 		return true;
202 
203 	return false;
204 }
205 
206 static int hook_unix_stream_connect(struct sock *const sock,
207 				    struct sock *const other,
208 				    struct sock *const newsk)
209 {
210 	const struct landlock_ruleset *const dom =
211 		landlock_get_current_domain();
212 
213 	/* Quick return for non-landlocked tasks. */
214 	if (!dom)
215 		return 0;
216 
217 	if (is_abstract_socket(other) && sock_is_scoped(other, dom))
218 		return -EPERM;
219 
220 	return 0;
221 }
222 
223 static int hook_unix_may_send(struct socket *const sock,
224 			      struct socket *const other)
225 {
226 	const struct landlock_ruleset *const dom =
227 		landlock_get_current_domain();
228 
229 	if (!dom)
230 		return 0;
231 
232 	/*
233 	 * Checks if this datagram socket was already allowed to be connected
234 	 * to other.
235 	 */
236 	if (unix_peer(sock->sk) == other->sk)
237 		return 0;
238 
239 	if (is_abstract_socket(other->sk) && sock_is_scoped(other->sk, dom))
240 		return -EPERM;
241 
242 	return 0;
243 }
244 
245 static struct security_hook_list landlock_hooks[] __ro_after_init = {
246 	LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check),
247 	LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
248 
249 	LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect),
250 	LSM_HOOK_INIT(unix_may_send, hook_unix_may_send),
251 };
252 
253 __init void landlock_add_task_hooks(void)
254 {
255 	security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
256 			   &landlock_lsmid);
257 }
258