Skip to content

Commit e1b061b

Browse files
committed
Merge tag 'landlock-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull landlock updates from Mickaël Salaün: "We can now scope a Landlock domain thanks to a new "scoped" field that can deny interactions with resources outside of this domain. The LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET flag denies connections to an abstract UNIX socket created outside of the current scoped domain, and the LANDLOCK_SCOPE_SIGNAL flag denies sending a signal to processes outside of the current scoped domain. These restrictions also apply to nested domains according to their scope. The related changes will also be useful to support other kind of IPC isolations" * tag 'landlock-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux: landlock: Document LANDLOCK_SCOPE_SIGNAL samples/landlock: Add support for signal scoping selftests/landlock: Test signal created by out-of-bound message selftests/landlock: Test signal scoping for threads selftests/landlock: Test signal scoping landlock: Add signal scoping landlock: Document LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET samples/landlock: Add support for abstract UNIX socket scoping selftests/landlock: Test inherited restriction of abstract UNIX socket selftests/landlock: Test connected and unconnected datagram UNIX socket selftests/landlock: Test UNIX sockets with any address formats selftests/landlock: Test abstract UNIX socket scoping selftests/landlock: Test handling of unknown scope landlock: Add abstract UNIX socket scoping
2 parents 24f772d + 1ca9808 commit e1b061b

File tree

21 files changed

+2359
-47
lines changed

21 files changed

+2359
-47
lines changed

Documentation/userspace-api/landlock.rst

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Landlock: unprivileged access control
88
=====================================
99

1010
:Author: Mickaël Salaün
11-
:Date: July 2024
11+
:Date: September 2024
1212

1313
The goal of Landlock is to enable to restrict ambient rights (e.g. global
1414
filesystem or network access) for a set of processes. Because Landlock
@@ -81,6 +81,9 @@ to be explicit about the denied-by-default access rights.
8181
.handled_access_net =
8282
LANDLOCK_ACCESS_NET_BIND_TCP |
8383
LANDLOCK_ACCESS_NET_CONNECT_TCP,
84+
.scoped =
85+
LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
86+
LANDLOCK_SCOPE_SIGNAL,
8487
};
8588
8689
Because we may not know on which kernel version an application will be
@@ -119,6 +122,11 @@ version, and only use the available subset of access rights:
119122
case 4:
120123
/* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
121124
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
125+
__attribute__((fallthrough));
126+
case 5:
127+
/* Removes LANDLOCK_SCOPE_* for ABI < 6 */
128+
ruleset_attr.scoped &= ~(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
129+
LANDLOCK_SCOPE_SIGNAL);
122130
}
123131
124132
This enables to create an inclusive ruleset that will contain our rules.
@@ -306,6 +314,38 @@ To be allowed to use :manpage:`ptrace(2)` and related syscalls on a target
306314
process, a sandboxed process should have a subset of the target process rules,
307315
which means the tracee must be in a sub-domain of the tracer.
308316

317+
IPC scoping
318+
-----------
319+
320+
Similar to the implicit `Ptrace restrictions`_, we may want to further restrict
321+
interactions between sandboxes. Each Landlock domain can be explicitly scoped
322+
for a set of actions by specifying it on a ruleset. For example, if a
323+
sandboxed process should not be able to :manpage:`connect(2)` to a
324+
non-sandboxed process through abstract :manpage:`unix(7)` sockets, we can
325+
specify such restriction with ``LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET``.
326+
Moreover, if a sandboxed process should not be able to send a signal to a
327+
non-sandboxed process, we can specify this restriction with
328+
``LANDLOCK_SCOPE_SIGNAL``.
329+
330+
A sandboxed process can connect to a non-sandboxed process when its domain is
331+
not scoped. If a process's domain is scoped, it can only connect to sockets
332+
created by processes in the same scope.
333+
Moreover, If a process is scoped to send signal to a non-scoped process, it can
334+
only send signals to processes in the same scope.
335+
336+
A connected datagram socket behaves like a stream socket when its domain is
337+
scoped, meaning if the domain is scoped after the socket is connected , it can
338+
still :manpage:`send(2)` data just like a stream socket. However, in the same
339+
scenario, a non-connected datagram socket cannot send data (with
340+
:manpage:`sendto(2)`) outside its scope.
341+
342+
A process with a scoped domain can inherit a socket created by a non-scoped
343+
process. The process cannot connect to this socket since it has a scoped
344+
domain.
345+
346+
IPC scoping does not support exceptions, so if a domain is scoped, no rules can
347+
be added to allow access to resources or processes outside of the scope.
348+
309349
Truncating files
310350
----------------
311351

@@ -404,7 +444,7 @@ Access rights
404444
-------------
405445

406446
.. kernel-doc:: include/uapi/linux/landlock.h
407-
:identifiers: fs_access net_access
447+
:identifiers: fs_access net_access scope
408448

409449
Creating a new ruleset
410450
----------------------
@@ -541,6 +581,20 @@ earlier ABI.
541581
Starting with the Landlock ABI version 5, it is possible to restrict the use of
542582
:manpage:`ioctl(2)` using the new ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right.
543583

584+
Abstract UNIX socket scoping (ABI < 6)
585+
--------------------------------------
586+
587+
Starting with the Landlock ABI version 6, it is possible to restrict
588+
connections to an abstract :manpage:`unix(7)` socket by setting
589+
``LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET`` to the ``scoped`` ruleset attribute.
590+
591+
Signal scoping (ABI < 6)
592+
------------------------
593+
594+
Starting with the Landlock ABI version 6, it is possible to restrict
595+
:manpage:`signal(7)` sending by setting ``LANDLOCK_SCOPE_SIGNAL`` to the
596+
``scoped`` ruleset attribute.
597+
544598
.. _kernel_support:
545599

546600
Kernel support

include/uapi/linux/landlock.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ struct landlock_ruleset_attr {
4444
* flags`_).
4545
*/
4646
__u64 handled_access_net;
47+
/**
48+
* @scoped: Bitmask of scopes (cf. `Scope flags`_)
49+
* restricting a Landlock domain from accessing outside
50+
* resources (e.g. IPCs).
51+
*/
52+
__u64 scoped;
4753
};
4854

4955
/*
@@ -274,4 +280,28 @@ struct landlock_net_port_attr {
274280
#define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0)
275281
#define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1)
276282
/* clang-format on */
283+
284+
/**
285+
* DOC: scope
286+
*
287+
* Scope flags
288+
* ~~~~~~~~~~~
289+
*
290+
* These flags enable to isolate a sandboxed process from a set of IPC actions.
291+
* Setting a flag for a ruleset will isolate the Landlock domain to forbid
292+
* connections to resources outside the domain.
293+
*
294+
* Scopes:
295+
*
296+
* - %LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET: Restrict a sandboxed process from
297+
* connecting to an abstract UNIX socket created by a process outside the
298+
* related Landlock domain (e.g. a parent domain or a non-sandboxed process).
299+
* - %LANDLOCK_SCOPE_SIGNAL: Restrict a sandboxed process from sending a signal
300+
* to another process outside the domain.
301+
*/
302+
/* clang-format off */
303+
#define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET (1ULL << 0)
304+
#define LANDLOCK_SCOPE_SIGNAL (1ULL << 1)
305+
/* clang-format on*/
306+
277307
#endif /* _UAPI_LINUX_LANDLOCK_H */

samples/landlock/sandboxer.c

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <fcntl.h>
1515
#include <linux/landlock.h>
1616
#include <linux/prctl.h>
17+
#include <linux/socket.h>
1718
#include <stddef.h>
1819
#include <stdio.h>
1920
#include <stdlib.h>
@@ -22,6 +23,7 @@
2223
#include <sys/stat.h>
2324
#include <sys/syscall.h>
2425
#include <unistd.h>
26+
#include <stdbool.h>
2527

2628
#ifndef landlock_create_ruleset
2729
static inline int
@@ -55,6 +57,7 @@ static inline int landlock_restrict_self(const int ruleset_fd,
5557
#define ENV_FS_RW_NAME "LL_FS_RW"
5658
#define ENV_TCP_BIND_NAME "LL_TCP_BIND"
5759
#define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT"
60+
#define ENV_SCOPED_NAME "LL_SCOPED"
5861
#define ENV_DELIMITER ":"
5962

6063
static int parse_path(char *env_path, const char ***const path_list)
@@ -184,6 +187,55 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
184187
return ret;
185188
}
186189

190+
/* Returns true on error, false otherwise. */
191+
static bool check_ruleset_scope(const char *const env_var,
192+
struct landlock_ruleset_attr *ruleset_attr)
193+
{
194+
char *env_type_scope, *env_type_scope_next, *ipc_scoping_name;
195+
bool error = false;
196+
bool abstract_scoping = false;
197+
bool signal_scoping = false;
198+
199+
/* Scoping is not supported by Landlock ABI */
200+
if (!(ruleset_attr->scoped &
201+
(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL)))
202+
goto out_unset;
203+
204+
env_type_scope = getenv(env_var);
205+
/* Scoping is not supported by the user */
206+
if (!env_type_scope || strcmp("", env_type_scope) == 0)
207+
goto out_unset;
208+
209+
env_type_scope = strdup(env_type_scope);
210+
env_type_scope_next = env_type_scope;
211+
while ((ipc_scoping_name =
212+
strsep(&env_type_scope_next, ENV_DELIMITER))) {
213+
if (strcmp("a", ipc_scoping_name) == 0 && !abstract_scoping) {
214+
abstract_scoping = true;
215+
} else if (strcmp("s", ipc_scoping_name) == 0 &&
216+
!signal_scoping) {
217+
signal_scoping = true;
218+
} else {
219+
fprintf(stderr, "Unknown or duplicate scope \"%s\"\n",
220+
ipc_scoping_name);
221+
error = true;
222+
goto out_free_name;
223+
}
224+
}
225+
226+
out_free_name:
227+
free(env_type_scope);
228+
229+
out_unset:
230+
if (!abstract_scoping)
231+
ruleset_attr->scoped &= ~LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET;
232+
if (!signal_scoping)
233+
ruleset_attr->scoped &= ~LANDLOCK_SCOPE_SIGNAL;
234+
235+
unsetenv(env_var);
236+
return error;
237+
}
238+
187239
/* clang-format off */
188240

189241
#define ACCESS_FS_ROUGHLY_READ ( \
@@ -208,7 +260,7 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
208260

209261
/* clang-format on */
210262

211-
#define LANDLOCK_ABI_LAST 5
263+
#define LANDLOCK_ABI_LAST 6
212264

213265
int main(const int argc, char *const argv[], char *const *const envp)
214266
{
@@ -223,14 +275,16 @@ int main(const int argc, char *const argv[], char *const *const envp)
223275
.handled_access_fs = access_fs_rw,
224276
.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
225277
LANDLOCK_ACCESS_NET_CONNECT_TCP,
278+
.scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
279+
LANDLOCK_SCOPE_SIGNAL,
226280
};
227281

228282
if (argc < 2) {
229283
fprintf(stderr,
230-
"usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s "
284+
"usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s "
231285
"<cmd> [args]...\n\n",
232286
ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
233-
ENV_TCP_CONNECT_NAME, argv[0]);
287+
ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]);
234288
fprintf(stderr,
235289
"Execute a command in a restricted environment.\n\n");
236290
fprintf(stderr,
@@ -251,15 +305,18 @@ int main(const int argc, char *const argv[], char *const *const envp)
251305
fprintf(stderr,
252306
"* %s: list of ports allowed to connect (client).\n",
253307
ENV_TCP_CONNECT_NAME);
308+
fprintf(stderr, "* %s: list of scoped IPCs.\n",
309+
ENV_SCOPED_NAME);
254310
fprintf(stderr,
255311
"\nexample:\n"
256312
"%s=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" "
257313
"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
258314
"%s=\"9418\" "
259315
"%s=\"80:443\" "
316+
"%s=\"a:s\" "
260317
"%s bash -i\n\n",
261318
ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
262-
ENV_TCP_CONNECT_NAME, argv[0]);
319+
ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]);
263320
fprintf(stderr,
264321
"This sandboxer can use Landlock features "
265322
"up to ABI version %d.\n",
@@ -327,6 +384,11 @@ int main(const int argc, char *const argv[], char *const *const envp)
327384
/* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
328385
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
329386

387+
__attribute__((fallthrough));
388+
case 5:
389+
/* Removes LANDLOCK_SCOPE_* for ABI < 6 */
390+
ruleset_attr.scoped &= ~(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
391+
LANDLOCK_SCOPE_SIGNAL);
330392
fprintf(stderr,
331393
"Hint: You should update the running kernel "
332394
"to leverage Landlock features "
@@ -358,6 +420,9 @@ int main(const int argc, char *const argv[], char *const *const envp)
358420
~LANDLOCK_ACCESS_NET_CONNECT_TCP;
359421
}
360422

423+
if (check_ruleset_scope(ENV_SCOPED_NAME, &ruleset_attr))
424+
return 1;
425+
361426
ruleset_fd =
362427
landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
363428
if (ruleset_fd < 0) {

security/landlock/cred.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ landlock_cred(const struct cred *cred)
2626
return cred->security + landlock_blob_sizes.lbs_cred;
2727
}
2828

29-
static inline const struct landlock_ruleset *landlock_get_current_domain(void)
29+
static inline struct landlock_ruleset *landlock_get_current_domain(void)
3030
{
3131
return landlock_cred(current_cred())->domain;
3232
}

security/landlock/fs.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,29 @@ static int hook_file_ioctl_compat(struct file *file, unsigned int cmd,
16391639
return -EACCES;
16401640
}
16411641

1642+
static void hook_file_set_fowner(struct file *file)
1643+
{
1644+
struct landlock_ruleset *new_dom, *prev_dom;
1645+
1646+
/*
1647+
* Lock already held by __f_setown(), see commit 26f204380a3c ("fs: Fix
1648+
* file_set_fowner LSM hook inconsistencies").
1649+
*/
1650+
lockdep_assert_held(&file_f_owner(file)->lock);
1651+
new_dom = landlock_get_current_domain();
1652+
landlock_get_ruleset(new_dom);
1653+
prev_dom = landlock_file(file)->fown_domain;
1654+
landlock_file(file)->fown_domain = new_dom;
1655+
1656+
/* Called in an RCU read-side critical section. */
1657+
landlock_put_ruleset_deferred(prev_dom);
1658+
}
1659+
1660+
static void hook_file_free_security(struct file *file)
1661+
{
1662+
landlock_put_ruleset_deferred(landlock_file(file)->fown_domain);
1663+
}
1664+
16421665
static struct security_hook_list landlock_hooks[] __ro_after_init = {
16431666
LSM_HOOK_INIT(inode_free_security_rcu, hook_inode_free_security_rcu),
16441667

@@ -1663,6 +1686,8 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
16631686
LSM_HOOK_INIT(file_truncate, hook_file_truncate),
16641687
LSM_HOOK_INIT(file_ioctl, hook_file_ioctl),
16651688
LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat),
1689+
LSM_HOOK_INIT(file_set_fowner, hook_file_set_fowner),
1690+
LSM_HOOK_INIT(file_free_security, hook_file_free_security),
16661691
};
16671692

16681693
__init void landlock_add_fs_hooks(void)

security/landlock/fs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ struct landlock_file_security {
5252
* needed to authorize later operations on the open file.
5353
*/
5454
access_mask_t allowed_access;
55+
/**
56+
* @fown_domain: Domain of the task that set the PID that may receive a
57+
* signal e.g., SIGURG when writing MSG_OOB to the related socket.
58+
* This pointer is protected by the related file->f_owner->lock, as for
59+
* fown_struct's members: pid, uid, and euid.
60+
*/
61+
struct landlock_ruleset *fown_domain;
5562
};
5663

5764
/**

security/landlock/limits.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
2727
#define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
2828

29+
#define LANDLOCK_LAST_SCOPE LANDLOCK_SCOPE_SIGNAL
30+
#define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1)
31+
#define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE)
2932
/* clang-format on */
3033

3134
#endif /* _SECURITY_LANDLOCK_LIMITS_H */

security/landlock/ruleset.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,13 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
5252

5353
struct landlock_ruleset *
5454
landlock_create_ruleset(const access_mask_t fs_access_mask,
55-
const access_mask_t net_access_mask)
55+
const access_mask_t net_access_mask,
56+
const access_mask_t scope_mask)
5657
{
5758
struct landlock_ruleset *new_ruleset;
5859

5960
/* Informs about useless ruleset. */
60-
if (!fs_access_mask && !net_access_mask)
61+
if (!fs_access_mask && !net_access_mask && !scope_mask)
6162
return ERR_PTR(-ENOMSG);
6263
new_ruleset = create_ruleset(1);
6364
if (IS_ERR(new_ruleset))
@@ -66,6 +67,8 @@ landlock_create_ruleset(const access_mask_t fs_access_mask,
6667
landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
6768
if (net_access_mask)
6869
landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
70+
if (scope_mask)
71+
landlock_add_scope_mask(new_ruleset, scope_mask, 0);
6972
return new_ruleset;
7073
}
7174

0 commit comments

Comments
 (0)