landlock 0.1.0 → 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,369 @@
1
+ #include "../landlock_native.h"
2
+
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+ #include <limits.h>
7
+ #include <sys/resource.h>
8
+ #include <sys/stat.h>
9
+
10
+ #ifdef __linux__
11
+ #include <linux/audit.h>
12
+ #include <linux/filter.h>
13
+ #include <linux/seccomp.h>
14
+ #endif
15
+
16
+ #ifndef SECCOMP_RET_ALLOW
17
+ #define SECCOMP_RET_ALLOW 0x7fff0000U
18
+ #endif
19
+ #ifndef SECCOMP_RET_ERRNO
20
+ #define SECCOMP_RET_ERRNO 0x00050000U
21
+ #endif
22
+ #ifndef SECCOMP_SET_MODE_FILTER
23
+ #define SECCOMP_SET_MODE_FILTER 1
24
+ #endif
25
+
26
+ typedef struct {
27
+ char **items;
28
+ size_t len;
29
+ size_t cap;
30
+ } string_list;
31
+
32
+ typedef struct {
33
+ unsigned long long *items;
34
+ size_t len;
35
+ size_t cap;
36
+ } ull_list;
37
+
38
+ static void die(const char *message) {
39
+ perror(message);
40
+ _exit(126);
41
+ }
42
+
43
+ static void die_msg(const char *message) {
44
+ fprintf(stderr, "landlock-safe-exec: %s\n", message);
45
+ _exit(126);
46
+ }
47
+
48
+ static void string_list_push(string_list *list, char *value) {
49
+ if (list->len == list->cap) {
50
+ size_t cap = list->cap ? list->cap * 2 : 8;
51
+ char **items = realloc(list->items, cap * sizeof(char *));
52
+ if (!items) die("realloc");
53
+ list->items = items;
54
+ list->cap = cap;
55
+ }
56
+ list->items[list->len++] = value;
57
+ }
58
+
59
+ static void ull_list_push(ull_list *list, unsigned long long value) {
60
+ if (list->len == list->cap) {
61
+ size_t cap = list->cap ? list->cap * 2 : 8;
62
+ unsigned long long *items = realloc(list->items, cap * sizeof(unsigned long long));
63
+ if (!items) die("realloc");
64
+ list->items = items;
65
+ list->cap = cap;
66
+ }
67
+ list->items[list->len++] = value;
68
+ }
69
+
70
+ static unsigned long long parse_ull(const char *value, const char *name) {
71
+ if (!value || value[0] == '\0' || value[0] == '-') die_msg(name);
72
+
73
+ errno = 0;
74
+ char *end = NULL;
75
+ unsigned long long parsed = strtoull(value, &end, 10);
76
+ if (errno == ERANGE || !end || *end != '\0') die_msg(name);
77
+
78
+ return parsed;
79
+ }
80
+
81
+ static unsigned long long parse_port(const char *value) {
82
+ unsigned long long port = parse_ull(value, "TCP port must be an integer between 0 and 65535");
83
+ if (port > 65535ULL) die_msg("TCP port must be between 0 and 65535");
84
+ return port;
85
+ }
86
+
87
+ static int abi_version(void) {
88
+ long abi = ll_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
89
+ if (abi < 0 && (errno == ENOSYS || errno == EOPNOTSUPP)) return 0;
90
+ if (abi < 0) die("landlock_create_ruleset(version)");
91
+ return (int)abi;
92
+ }
93
+
94
+ static uint64_t known_fs_rights_for_abi(int abi) {
95
+ uint64_t rights = LANDLOCK_ACCESS_FS_EXECUTE |
96
+ LANDLOCK_ACCESS_FS_WRITE_FILE |
97
+ LANDLOCK_ACCESS_FS_READ_FILE |
98
+ LANDLOCK_ACCESS_FS_READ_DIR |
99
+ LANDLOCK_ACCESS_FS_REMOVE_DIR |
100
+ LANDLOCK_ACCESS_FS_REMOVE_FILE |
101
+ LANDLOCK_ACCESS_FS_MAKE_CHAR |
102
+ LANDLOCK_ACCESS_FS_MAKE_DIR |
103
+ LANDLOCK_ACCESS_FS_MAKE_REG |
104
+ LANDLOCK_ACCESS_FS_MAKE_SOCK |
105
+ LANDLOCK_ACCESS_FS_MAKE_FIFO |
106
+ LANDLOCK_ACCESS_FS_MAKE_BLOCK |
107
+ LANDLOCK_ACCESS_FS_MAKE_SYM;
108
+ if (abi >= 2) rights |= LANDLOCK_ACCESS_FS_REFER;
109
+ if (abi >= 3) rights |= LANDLOCK_ACCESS_FS_TRUNCATE;
110
+ if (abi >= 5) rights |= LANDLOCK_ACCESS_FS_IOCTL_DEV;
111
+ return rights;
112
+ }
113
+
114
+ static uint64_t read_rights(void) {
115
+ return LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR;
116
+ }
117
+
118
+ static uint64_t execute_rights(void) {
119
+ return LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR;
120
+ }
121
+
122
+ static uint64_t write_rights(int abi) {
123
+ return known_fs_rights_for_abi(abi) & ~LANDLOCK_ACCESS_FS_EXECUTE;
124
+ }
125
+
126
+ static uint64_t file_path_rights(void) {
127
+ return LANDLOCK_ACCESS_FS_EXECUTE |
128
+ LANDLOCK_ACCESS_FS_WRITE_FILE |
129
+ LANDLOCK_ACCESS_FS_READ_FILE |
130
+ LANDLOCK_ACCESS_FS_TRUNCATE |
131
+ LANDLOCK_ACCESS_FS_IOCTL_DEV;
132
+ }
133
+
134
+ static void add_path_rule(int fd, const char *path, uint64_t rights) {
135
+ int parent_fd = open(path, O_PATH | O_CLOEXEC);
136
+ if (parent_fd < 0) die("open(path rule)");
137
+
138
+ struct stat st;
139
+ if (fstat(parent_fd, &st) != 0) die("fstat(path rule)");
140
+ if (!S_ISDIR(st.st_mode)) rights &= file_path_rights();
141
+
142
+ struct rb_landlock_path_beneath_attr rule;
143
+ memset(&rule, 0, sizeof(rule));
144
+ rule.allowed_access = rights;
145
+ rule.parent_fd = parent_fd;
146
+
147
+ long ret = ll_add_rule(fd, LANDLOCK_RULE_PATH_BENEATH, &rule, 0);
148
+ int saved_errno = errno;
149
+ close(parent_fd);
150
+ if (ret < 0) {
151
+ errno = saved_errno;
152
+ die("landlock_add_rule(path_beneath)");
153
+ }
154
+ }
155
+
156
+ static void add_net_rule(int fd, unsigned long long port, uint64_t rights) {
157
+ if (port > 65535ULL) die_msg("TCP port must be between 0 and 65535");
158
+ struct rb_landlock_net_port_attr rule;
159
+ memset(&rule, 0, sizeof(rule));
160
+ rule.allowed_access = rights;
161
+ rule.port = port;
162
+ if (ll_add_rule(fd, LANDLOCK_RULE_NET_PORT, &rule, 0) < 0) die("landlock_add_rule(net_port)");
163
+ }
164
+
165
+ static void apply_landlock(string_list *read_paths, string_list *write_paths, string_list *execute_paths,
166
+ ull_list *connect_ports, ull_list *bind_ports, int allow_all_known) {
167
+ int need_fs = read_paths->len || write_paths->len || execute_paths->len || allow_all_known;
168
+ int need_net = connect_ports->len || bind_ports->len;
169
+ if (!need_fs && !need_net) return;
170
+
171
+ int abi = abi_version();
172
+ if (abi <= 0) die_msg("Linux Landlock is unavailable");
173
+ if (need_net && abi < 4) die_msg("Landlock network rules require ABI v4+");
174
+
175
+ uint64_t fs_handled = allow_all_known ? known_fs_rights_for_abi(abi) : 0;
176
+ if (!allow_all_known) {
177
+ if (read_paths->len) fs_handled |= read_rights();
178
+ if (execute_paths->len) fs_handled |= execute_rights();
179
+ if (write_paths->len) fs_handled |= write_rights(abi);
180
+ }
181
+ uint64_t net_handled = 0;
182
+ if (bind_ports->len) net_handled |= LANDLOCK_ACCESS_NET_BIND_TCP;
183
+ if (connect_ports->len) net_handled |= LANDLOCK_ACCESS_NET_CONNECT_TCP;
184
+
185
+ struct rb_landlock_ruleset_attr attr;
186
+ memset(&attr, 0, sizeof(attr));
187
+ attr.handled_access_fs = fs_handled;
188
+ attr.handled_access_net = net_handled;
189
+
190
+ size_t attr_size = net_handled ? offsetof(struct rb_landlock_ruleset_attr, scoped) : offsetof(struct rb_landlock_ruleset_attr, handled_access_net);
191
+ int fd = (int)ll_create_ruleset(&attr, attr_size, 0);
192
+ if (fd < 0) die("landlock_create_ruleset");
193
+
194
+ for (size_t i = 0; i < read_paths->len; i++) add_path_rule(fd, read_paths->items[i], read_rights());
195
+ for (size_t i = 0; i < execute_paths->len; i++) add_path_rule(fd, execute_paths->items[i], execute_rights());
196
+ for (size_t i = 0; i < write_paths->len; i++) add_path_rule(fd, write_paths->items[i], write_rights(abi));
197
+ for (size_t i = 0; i < connect_ports->len; i++) add_net_rule(fd, connect_ports->items[i], LANDLOCK_ACCESS_NET_CONNECT_TCP);
198
+ for (size_t i = 0; i < bind_ports->len; i++) add_net_rule(fd, bind_ports->items[i], LANDLOCK_ACCESS_NET_BIND_TCP);
199
+
200
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) die("prctl(PR_SET_NO_NEW_PRIVS)");
201
+ if (ll_restrict_self(fd, 0) < 0) die("landlock_restrict_self");
202
+ close(fd);
203
+ }
204
+
205
+ static void apply_rlimit(const char *spec) {
206
+ char *copy = strdup(spec);
207
+ if (!copy) die("strdup");
208
+ char *eq = strchr(copy, '=');
209
+ if (!eq) die_msg("rlimit must be name=value");
210
+ *eq = '\0';
211
+ unsigned long long value = parse_ull(eq + 1, "rlimit value must be a non-negative integer");
212
+ int resource = -1;
213
+
214
+ if (strcmp(copy, "cpu_seconds") == 0) resource = RLIMIT_CPU;
215
+ #ifdef RLIMIT_AS
216
+ else if (strcmp(copy, "memory_bytes") == 0) resource = RLIMIT_AS;
217
+ #endif
218
+ else if (strcmp(copy, "file_size_bytes") == 0) resource = RLIMIT_FSIZE;
219
+ else if (strcmp(copy, "open_files") == 0) resource = RLIMIT_NOFILE;
220
+ #ifdef RLIMIT_NPROC
221
+ else if (strcmp(copy, "processes") == 0) resource = RLIMIT_NPROC;
222
+ #endif
223
+ else die_msg("unknown rlimit");
224
+
225
+ struct rlimit limit;
226
+ limit.rlim_cur = (rlim_t)value;
227
+ limit.rlim_max = (rlim_t)value;
228
+ if (setrlimit(resource, &limit) != 0) die("setrlimit");
229
+ free(copy);
230
+ }
231
+
232
+ static int deny_syscalls[] = {
233
+ #ifdef __NR_socket
234
+ __NR_socket,
235
+ #endif
236
+ #ifdef __NR_socketpair
237
+ __NR_socketpair,
238
+ #endif
239
+ #ifdef __NR_connect
240
+ __NR_connect,
241
+ #endif
242
+ #ifdef __NR_bind
243
+ __NR_bind,
244
+ #endif
245
+ #ifdef __NR_listen
246
+ __NR_listen,
247
+ #endif
248
+ #ifdef __NR_accept
249
+ __NR_accept,
250
+ #endif
251
+ #ifdef __NR_accept4
252
+ __NR_accept4,
253
+ #endif
254
+ #ifdef __NR_sendto
255
+ __NR_sendto,
256
+ #endif
257
+ #ifdef __NR_sendmsg
258
+ __NR_sendmsg,
259
+ #endif
260
+ #ifdef __NR_sendmmsg
261
+ __NR_sendmmsg,
262
+ #endif
263
+ #ifdef __NR_recvfrom
264
+ __NR_recvfrom,
265
+ #endif
266
+ #ifdef __NR_recvmsg
267
+ __NR_recvmsg,
268
+ #endif
269
+ #ifdef __NR_recvmmsg
270
+ __NR_recvmmsg,
271
+ #endif
272
+ #ifdef __NR_socketcall
273
+ __NR_socketcall,
274
+ #endif
275
+ };
276
+
277
+ #if defined(__x86_64__) && defined(AUDIT_ARCH_X86_64)
278
+ #define EXPECTED_AUDIT_ARCH AUDIT_ARCH_X86_64
279
+ #elif defined(__aarch64__) && defined(AUDIT_ARCH_AARCH64)
280
+ #define EXPECTED_AUDIT_ARCH AUDIT_ARCH_AARCH64
281
+ #elif defined(__i386__) && defined(AUDIT_ARCH_I386)
282
+ #define EXPECTED_AUDIT_ARCH AUDIT_ARCH_I386
283
+ #endif
284
+
285
+ #ifndef SECCOMP_RET_KILL_PROCESS
286
+ #define SECCOMP_RET_KILL_PROCESS 0x80000000U
287
+ #endif
288
+
289
+ static void apply_seccomp_deny_network(void) {
290
+ size_t count = sizeof(deny_syscalls) / sizeof(deny_syscalls[0]);
291
+ if (count == 0) return;
292
+
293
+ size_t len = 1 + (2 * count) + 1;
294
+ #ifdef EXPECTED_AUDIT_ARCH
295
+ len += 3;
296
+ #endif
297
+ struct sock_filter *filter = calloc(len, sizeof(struct sock_filter));
298
+ if (!filter) die("calloc");
299
+
300
+ size_t pc = 0;
301
+ #ifdef EXPECTED_AUDIT_ARCH
302
+ filter[pc++] = (struct sock_filter)BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch));
303
+ filter[pc++] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, EXPECTED_AUDIT_ARCH, 1, 0);
304
+ filter[pc++] = (struct sock_filter)BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS);
305
+ #endif
306
+ filter[pc++] = (struct sock_filter)BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr));
307
+ for (size_t i = 0; i < count; i++) {
308
+ filter[pc++] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (unsigned int)deny_syscalls[i], 0, 1);
309
+ filter[pc++] = (struct sock_filter)BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EPERM);
310
+ }
311
+ filter[pc++] = (struct sock_filter)BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
312
+
313
+ struct sock_fprog prog;
314
+ prog.len = (unsigned short)pc;
315
+ prog.filter = filter;
316
+
317
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) die("prctl(PR_SET_NO_NEW_PRIVS)");
318
+ #ifdef SYS_seccomp
319
+ if (syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog) != 0)
320
+ #endif
321
+ {
322
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) die("seccomp(SECCOMP_SET_MODE_FILTER)");
323
+ }
324
+ free(filter);
325
+ }
326
+
327
+ static char *require_arg(int argc, char **argv, int *i) {
328
+ if (*i + 1 >= argc) die_msg("missing option argument");
329
+ (*i)++;
330
+ return argv[*i];
331
+ }
332
+
333
+ int main(int argc, char **argv) {
334
+ string_list read_paths = {0}, write_paths = {0}, execute_paths = {0}, env_vars = {0};
335
+ ull_list connect_ports = {0}, bind_ports = {0};
336
+ int unsetenv_others = 0, seccomp_deny_network = 0, allow_all_known = 0;
337
+ char *chdir_path = NULL;
338
+ int command_index = -1;
339
+
340
+ for (int i = 1; i < argc; i++) {
341
+ if (strcmp(argv[i], "--") == 0) { command_index = i + 1; break; }
342
+ if (strcmp(argv[i], "--read") == 0) string_list_push(&read_paths, require_arg(argc, argv, &i));
343
+ else if (strcmp(argv[i], "--write") == 0) string_list_push(&write_paths, require_arg(argc, argv, &i));
344
+ else if (strcmp(argv[i], "--execute") == 0) string_list_push(&execute_paths, require_arg(argc, argv, &i));
345
+ else if (strcmp(argv[i], "--connect-tcp") == 0) ull_list_push(&connect_ports, parse_port(require_arg(argc, argv, &i)));
346
+ else if (strcmp(argv[i], "--bind-tcp") == 0) ull_list_push(&bind_ports, parse_port(require_arg(argc, argv, &i)));
347
+ else if (strcmp(argv[i], "--chdir") == 0) chdir_path = require_arg(argc, argv, &i);
348
+ else if (strcmp(argv[i], "--env") == 0) string_list_push(&env_vars, require_arg(argc, argv, &i));
349
+ else if (strcmp(argv[i], "--unsetenv-others") == 0) unsetenv_others = 1;
350
+ else if (strcmp(argv[i], "--rlimit") == 0) apply_rlimit(require_arg(argc, argv, &i));
351
+ else if (strcmp(argv[i], "--seccomp-deny-network") == 0) seccomp_deny_network = 1;
352
+ else if (strcmp(argv[i], "--allow-all-known") == 0) allow_all_known = 1;
353
+ else die_msg("unknown option");
354
+ }
355
+
356
+ if (command_index < 0 || command_index >= argc) die_msg("missing command after --");
357
+
358
+ if (chdir_path && chdir(chdir_path) != 0) die("chdir");
359
+ if (unsetenv_others && clearenv() != 0) die("clearenv");
360
+ for (size_t i = 0; i < env_vars.len; i++) {
361
+ if (putenv(env_vars.items[i]) != 0) die("putenv");
362
+ }
363
+
364
+ apply_landlock(&read_paths, &write_paths, &execute_paths, &connect_ports, &bind_ports, allow_all_known);
365
+ if (seccomp_deny_network) apply_seccomp_deny_network();
366
+
367
+ execvp(argv[command_index], &argv[command_index]);
368
+ die("execvp");
369
+ }
@@ -5,8 +5,38 @@ require "mkmf"
5
5
  abort "missing ruby headers" unless have_header("ruby.h")
6
6
 
7
7
  have_header("linux/landlock.h")
8
+ have_header("linux/seccomp.h")
9
+ have_header("linux/filter.h")
8
10
  have_header("sys/prctl.h")
9
11
  have_header("sys/syscall.h")
12
+ have_header("sys/resource.h")
10
13
  have_header("fcntl.h")
11
14
 
12
15
  create_makefile("landlock/landlock")
16
+
17
+ if RUBY_PLATFORM.include?("linux")
18
+ helper = "landlock-safe-exec"
19
+ helper_src = "$(srcdir)/bin/safe_exec_helper.c"
20
+ helper_dest = "$(RUBYARCHDIR)/#{helper}"
21
+
22
+ File.open("Makefile", "a") do |makefile|
23
+ makefile.puts <<~MAKE
24
+
25
+ all: #{helper}
26
+
27
+ #{helper}: #{helper_src}
28
+ \t$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) #{helper_src} -o #{helper} $(LIBS)
29
+
30
+ install: install-#{helper}
31
+
32
+ install-#{helper}: #{helper}
33
+ \t$(MAKEDIRS) $(RUBYARCHDIR)
34
+ \t$(INSTALL_PROG) #{helper} #{helper_dest}
35
+
36
+ clean-local::
37
+ \t$(Q)$(RM) #{helper}
38
+
39
+ clean: clean-local
40
+ MAKE
41
+ end
42
+ end
@@ -1,157 +1,12 @@
1
1
  #include "ruby.h"
2
+ #include "landlock_native.h"
2
3
 
3
- #include <errno.h>
4
- #include <fcntl.h>
5
- #include <stdint.h>
6
- #include <stddef.h>
7
4
  #include <string.h>
8
- #include <unistd.h>
9
- #include <sys/prctl.h>
10
- #include <sys/syscall.h>
11
-
12
- #ifdef HAVE_LINUX_LANDLOCK_H
13
- #include <linux/landlock.h>
14
- #endif
15
-
16
- #ifndef SYS_landlock_create_ruleset
17
- # if defined(__x86_64__)
18
- # define SYS_landlock_create_ruleset 444
19
- # define SYS_landlock_add_rule 445
20
- # define SYS_landlock_restrict_self 446
21
- # elif defined(__aarch64__)
22
- # define SYS_landlock_create_ruleset 444
23
- # define SYS_landlock_add_rule 445
24
- # define SYS_landlock_restrict_self 446
25
- # elif defined(__i386__)
26
- # define SYS_landlock_create_ruleset 451
27
- # define SYS_landlock_add_rule 452
28
- # define SYS_landlock_restrict_self 453
29
- # endif
30
- #endif
31
-
32
- #ifndef LANDLOCK_CREATE_RULESET_VERSION
33
- #define LANDLOCK_CREATE_RULESET_VERSION (1U << 0)
34
- #endif
35
-
36
- #ifndef LANDLOCK_RULE_PATH_BENEATH
37
- #define LANDLOCK_RULE_PATH_BENEATH 1
38
- #endif
39
-
40
- #ifndef LANDLOCK_RULE_NET_PORT
41
- #define LANDLOCK_RULE_NET_PORT 2
42
- #endif
43
-
44
- #ifndef LANDLOCK_ACCESS_FS_EXECUTE
45
- #define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0)
46
- #endif
47
- #ifndef LANDLOCK_ACCESS_FS_WRITE_FILE
48
- #define LANDLOCK_ACCESS_FS_WRITE_FILE (1ULL << 1)
49
- #endif
50
- #ifndef LANDLOCK_ACCESS_FS_READ_FILE
51
- #define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2)
52
- #endif
53
- #ifndef LANDLOCK_ACCESS_FS_READ_DIR
54
- #define LANDLOCK_ACCESS_FS_READ_DIR (1ULL << 3)
55
- #endif
56
- #ifndef LANDLOCK_ACCESS_FS_REMOVE_DIR
57
- #define LANDLOCK_ACCESS_FS_REMOVE_DIR (1ULL << 4)
58
- #endif
59
- #ifndef LANDLOCK_ACCESS_FS_REMOVE_FILE
60
- #define LANDLOCK_ACCESS_FS_REMOVE_FILE (1ULL << 5)
61
- #endif
62
- #ifndef LANDLOCK_ACCESS_FS_MAKE_CHAR
63
- #define LANDLOCK_ACCESS_FS_MAKE_CHAR (1ULL << 6)
64
- #endif
65
- #ifndef LANDLOCK_ACCESS_FS_MAKE_DIR
66
- #define LANDLOCK_ACCESS_FS_MAKE_DIR (1ULL << 7)
67
- #endif
68
- #ifndef LANDLOCK_ACCESS_FS_MAKE_REG
69
- #define LANDLOCK_ACCESS_FS_MAKE_REG (1ULL << 8)
70
- #endif
71
- #ifndef LANDLOCK_ACCESS_FS_MAKE_SOCK
72
- #define LANDLOCK_ACCESS_FS_MAKE_SOCK (1ULL << 9)
73
- #endif
74
- #ifndef LANDLOCK_ACCESS_FS_MAKE_FIFO
75
- #define LANDLOCK_ACCESS_FS_MAKE_FIFO (1ULL << 10)
76
- #endif
77
- #ifndef LANDLOCK_ACCESS_FS_MAKE_BLOCK
78
- #define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11)
79
- #endif
80
- #ifndef LANDLOCK_ACCESS_FS_MAKE_SYM
81
- #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12)
82
- #endif
83
- #ifndef LANDLOCK_ACCESS_FS_REFER
84
- #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
85
- #endif
86
- #ifndef LANDLOCK_ACCESS_FS_TRUNCATE
87
- #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
88
- #endif
89
- #ifndef LANDLOCK_ACCESS_FS_IOCTL_DEV
90
- #define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15)
91
- #endif
92
-
93
- #ifndef LANDLOCK_ACCESS_NET_BIND_TCP
94
- #define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0)
95
- #endif
96
- #ifndef LANDLOCK_ACCESS_NET_CONNECT_TCP
97
- #define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1)
98
- #endif
99
-
100
- #ifndef O_PATH
101
- #define O_PATH 010000000
102
- #endif
103
-
104
- #ifndef O_CLOEXEC
105
- #define O_CLOEXEC 02000000
106
- #endif
107
-
108
- struct rb_landlock_ruleset_attr {
109
- uint64_t handled_access_fs;
110
- uint64_t handled_access_net;
111
- uint64_t scoped;
112
- };
113
-
114
- struct rb_landlock_path_beneath_attr {
115
- uint64_t allowed_access;
116
- int32_t parent_fd;
117
- } __attribute__((packed));
118
-
119
- struct rb_landlock_net_port_attr {
120
- uint64_t allowed_access;
121
- uint64_t port;
122
- };
123
5
 
124
6
  static VALUE mLandlock;
125
7
  static VALUE eLandlockError;
126
8
  static VALUE eSyscallError;
127
9
 
128
- static long ll_create_ruleset(const void *attr, size_t size, uint32_t flags) {
129
- #ifdef SYS_landlock_create_ruleset
130
- return syscall(SYS_landlock_create_ruleset, attr, size, flags);
131
- #else
132
- errno = ENOSYS;
133
- return -1;
134
- #endif
135
- }
136
-
137
- static long ll_add_rule(int ruleset_fd, int rule_type, const void *rule_attr, uint32_t flags) {
138
- #ifdef SYS_landlock_add_rule
139
- return syscall(SYS_landlock_add_rule, ruleset_fd, rule_type, rule_attr, flags);
140
- #else
141
- errno = ENOSYS;
142
- return -1;
143
- #endif
144
- }
145
-
146
- static long ll_restrict_self(int ruleset_fd, uint32_t flags) {
147
- #ifdef SYS_landlock_restrict_self
148
- return syscall(SYS_landlock_restrict_self, ruleset_fd, flags);
149
- #else
150
- errno = ENOSYS;
151
- return -1;
152
- #endif
153
- }
154
-
155
10
  static void raise_syscall_error(const char *syscall_name) {
156
11
  int saved_errno = errno;
157
12
  VALUE err = rb_funcall(eSyscallError, rb_intern("new"), 3,
@@ -170,16 +25,24 @@ static VALUE rb_ll_abi_version(VALUE self) {
170
25
  return LONG2NUM(abi);
171
26
  }
172
27
 
173
- static VALUE rb_ll_create_ruleset(VALUE self, VALUE fs_bits, VALUE net_bits) {
28
+ static VALUE rb_ll_create_ruleset(int argc, VALUE *argv, VALUE self) {
29
+ VALUE fs_bits, net_bits, scoped_bits;
30
+ rb_scan_args(argc, argv, "21", &fs_bits, &net_bits, &scoped_bits);
31
+
174
32
  struct rb_landlock_ruleset_attr attr;
175
33
  uint64_t handled_access_net = NUM2ULL(net_bits);
176
- size_t attr_size = handled_access_net == 0 ?
177
- offsetof(struct rb_landlock_ruleset_attr, handled_access_net) :
178
- offsetof(struct rb_landlock_ruleset_attr, scoped);
34
+ uint64_t scoped = NIL_P(scoped_bits) ? 0 : NUM2ULL(scoped_bits);
35
+ size_t attr_size = offsetof(struct rb_landlock_ruleset_attr, handled_access_net);
36
+ if (scoped != 0) {
37
+ attr_size = sizeof(struct rb_landlock_ruleset_attr);
38
+ } else if (handled_access_net != 0) {
39
+ attr_size = offsetof(struct rb_landlock_ruleset_attr, scoped);
40
+ }
179
41
 
180
42
  memset(&attr, 0, sizeof(attr));
181
43
  attr.handled_access_fs = NUM2ULL(fs_bits);
182
44
  attr.handled_access_net = handled_access_net;
45
+ attr.scoped = scoped;
183
46
 
184
47
  long fd = ll_create_ruleset(&attr, attr_size, 0);
185
48
  if (fd < 0) raise_syscall_error("landlock_create_ruleset");
@@ -187,6 +50,8 @@ static VALUE rb_ll_create_ruleset(VALUE self, VALUE fs_bits, VALUE net_bits) {
187
50
  }
188
51
 
189
52
  static VALUE rb_ll_add_path_rule(VALUE self, VALUE ruleset_fd, VALUE path, VALUE access_bits) {
53
+ int ruleset = NUM2INT(ruleset_fd);
54
+ uint64_t allowed_access = NUM2ULL(access_bits);
190
55
  Check_Type(path, T_STRING);
191
56
  const char *cpath = StringValueCStr(path);
192
57
  int parent_fd = open(cpath, O_PATH | O_CLOEXEC);
@@ -194,10 +59,10 @@ static VALUE rb_ll_add_path_rule(VALUE self, VALUE ruleset_fd, VALUE path, VALUE
194
59
 
195
60
  struct rb_landlock_path_beneath_attr rule;
196
61
  memset(&rule, 0, sizeof(rule));
197
- rule.allowed_access = NUM2ULL(access_bits);
62
+ rule.allowed_access = allowed_access;
198
63
  rule.parent_fd = parent_fd;
199
64
 
200
- long ret = ll_add_rule(NUM2INT(ruleset_fd), LANDLOCK_RULE_PATH_BENEATH, &rule, 0);
65
+ long ret = ll_add_rule(ruleset, LANDLOCK_RULE_PATH_BENEATH, &rule, 0);
201
66
  int saved_errno = errno;
202
67
  close(parent_fd);
203
68
  if (ret < 0) {
@@ -222,6 +87,7 @@ static VALUE rb_ll_add_net_rule(VALUE self, VALUE ruleset_fd, VALUE port, VALUE
222
87
  }
223
88
 
224
89
  static VALUE rb_ll_restrict_self(VALUE self, VALUE ruleset_fd) {
90
+ #ifdef __linux__
225
91
  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
226
92
  raise_syscall_error("prctl(PR_SET_NO_NEW_PRIVS)");
227
93
  }
@@ -229,6 +95,10 @@ static VALUE rb_ll_restrict_self(VALUE self, VALUE ruleset_fd) {
229
95
  long ret = ll_restrict_self(NUM2INT(ruleset_fd), 0);
230
96
  if (ret < 0) raise_syscall_error("landlock_restrict_self");
231
97
  return Qtrue;
98
+ #else
99
+ errno = ENOSYS;
100
+ raise_syscall_error("landlock_restrict_self");
101
+ #endif
232
102
  }
233
103
 
234
104
  static VALUE rb_ll_close_fd(VALUE self, VALUE fd_value) {
@@ -253,7 +123,7 @@ void Init_landlock(void) {
253
123
  }
254
124
 
255
125
  rb_define_singleton_method(mLandlock, "abi_version", rb_ll_abi_version, 0);
256
- rb_define_singleton_method(mLandlock, "_create_ruleset", rb_ll_create_ruleset, 2);
126
+ rb_define_singleton_method(mLandlock, "_create_ruleset", rb_ll_create_ruleset, -1);
257
127
  rb_define_singleton_method(mLandlock, "_add_path_rule", rb_ll_add_path_rule, 3);
258
128
  rb_define_singleton_method(mLandlock, "_add_net_rule", rb_ll_add_net_rule, 3);
259
129
  rb_define_singleton_method(mLandlock, "_restrict_self", rb_ll_restrict_self, 1);
@@ -277,4 +147,6 @@ void Init_landlock(void) {
277
147
  rb_define_const(mLandlock, "ACCESS_FS_IOCTL_DEV", ULL2NUM(LANDLOCK_ACCESS_FS_IOCTL_DEV));
278
148
  rb_define_const(mLandlock, "ACCESS_NET_BIND_TCP", ULL2NUM(LANDLOCK_ACCESS_NET_BIND_TCP));
279
149
  rb_define_const(mLandlock, "ACCESS_NET_CONNECT_TCP", ULL2NUM(LANDLOCK_ACCESS_NET_CONNECT_TCP));
150
+ rb_define_const(mLandlock, "SCOPE_ABSTRACT_UNIX_SOCKET", ULL2NUM(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET));
151
+ rb_define_const(mLandlock, "SCOPE_SIGNAL", ULL2NUM(LANDLOCK_SCOPE_SIGNAL));
280
152
  }