Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/security/landlock/task.c
29265 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Landlock - Ptrace and scope hooks
4
*
5
* Copyright © 2017-2020 Mickaël Salaün <[email protected]>
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
*/
41
static 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
63
static 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
*/
85
static 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
const struct landlock_ruleset *child_dom;
90
int err;
91
92
/* Quick return for non-landlocked tasks. */
93
parent_subject = landlock_cred(current_cred());
94
if (!parent_subject)
95
return 0;
96
97
scoped_guard(rcu)
98
{
99
child_dom = 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
*/
135
static 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 - Checks if the client domain is scoped in the same
170
* domain as the server.
171
*
172
* @client: IPC sender domain.
173
* @server: IPC receiver domain.
174
* @scope: The scope restriction criteria.
175
*
176
* Returns: True if the @client domain is scoped to access the @server,
177
* unless the @server is also scoped in the same domain as @client.
178
*/
179
static 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
235
static 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
247
static 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
261
static const struct access_masks unix_scope = {
262
.scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET,
263
};
264
265
static 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
297
static 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
334
static const struct access_masks signal_scope = {
335
.scope = LANDLOCK_SCOPE_SIGNAL,
336
};
337
338
static 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
390
static 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
433
static 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
}
449
450