1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
#define _GNU_SOURCE
#include "landlock_signatures.h"
#include "util.h"
#include "sandboxing.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/prctl.h>
#include <linux/prctl.h>
int create_new_landlock_ruleset(struct landlock_ruleset_attr* ruleset_attr) {
int abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
if (abi < 0) {
/* Degrades gracefully if Landlock is not handled. */
print("landlock is not enabled, sandboxing will not work\n");
return -1;
}
switch (abi) {
case 1:
/* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
ruleset_attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
case 2:
/* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
ruleset_attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
case 3:
/* Removes network support for ABI < 4 */
ruleset_attr->handled_access_net &=
~(LANDLOCK_ACCESS_NET_BIND_TCP |
LANDLOCK_ACCESS_NET_CONNECT_TCP);
case 4:
/* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
ruleset_attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
case 5:
/* Removes LANDLOCK_SCOPE_* for ABI < 6 */
ruleset_attr->scoped &= ~(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
LANDLOCK_SCOPE_SIGNAL);
case 6 ... 8:
/* Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX for ABI < 9 */
ruleset_attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_RESOLVE_UNIX;
}
int ruleset_fd = landlock_create_ruleset(ruleset_attr, sizeof(*ruleset_attr), 0);
if (ruleset_fd < 0) {
print("Failed to create landlock ruleset: %s", strerror(errno));
return -1;
}
return ruleset_fd;
}
bool landlock_add_path_rule(int landlock_fd, const struct landlock_ruleset_attr* ruleset_attr, const char* path, struct landlock_path_beneath_attr* path_beneath) {
path_beneath->allowed_access &= ruleset_attr->handled_access_fs;
if (path_beneath->allowed_access) {
path_beneath->parent_fd = open(path, O_PATH | O_CLOEXEC);
if (path_beneath->parent_fd < 0) {
print("Failed to open file %s: %s\n", path, strerror(errno));
return false;
}
int err = landlock_add_rule(landlock_fd, LANDLOCK_RULE_PATH_BENEATH, path_beneath, 0);
close(path_beneath->parent_fd);
if (err) {
print("Failed to update ruleset: %s\n", strerror(errno));
return false;
}
}
return true;
}
bool landlock_enforce(int landlock_fd) {
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
print("Failed to restrict privileges: %s\n", strerror(errno));
return false;
}
uint32_t restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON | LANDLOCK_RESTRICT_SELF_TSYNC;
int abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
switch (abi) {
case 1 ... 6:
/* Removes logging flags for ABI < 7 */
restrict_flags &= ~(LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF |
LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF);
case 7:
/*
* Removes multithreaded enforcement flag for ABI < 8
*
* WARNING: Without this flag, calling landlock_restrict_self(2) is
* only equivalent if the calling process is single-threaded. Below
* ABI v8 (and as of ABI v8, when not using this flag), a Landlock
* policy would only be enforced for the calling thread and its
* children (and not for all threads, including parents and siblings).
*/
restrict_flags &= ~LANDLOCK_RESTRICT_SELF_TSYNC;
}
if (landlock_restrict_self(landlock_fd, restrict_flags)) {
print("Failed to enforce ruleset: %s\n", strerror(errno));
return false;
}
close(landlock_fd);
return true;
}
|