commit e52b4bf
shtrophic
·
2024-12-05 16:24:04 +0000 UTC
parent 7e743e8
import landloc.h
M
Makefile
+2,
-1
1@@ -37,7 +37,8 @@ activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \
2 xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h snac.h \
3 http_codes.h
4 sandbox.o: sandbox.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h \
5- xs_glob.h xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h snac.h
6+ xs_glob.h xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h \
7+ landloc.h snac.h
8 data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \
9 xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h snac.h \
10 http_codes.h
+153,
-0
1@@ -0,0 +1,153 @@
2+/**
3+ * Zero-Clause BSD
4+ * ===============
5+ *
6+ * Copyright 2024 shtrophic <christoph@liebender.dev>
7+ *
8+ * Permission to use, copy, modify, and/or distribute this software for
9+ * any purpose with or without fee is hereby granted.
10+ *
11+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
12+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
13+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
14+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
15+ * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
16+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18+ *
19+ */
20+
21+/**
22+ * Usage example:
23+ *
24+
25+LL_BEGIN(my_sandbox_function, const char *rw_path) {
26+
27+ LL_PATH(rw_path, LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_EXECUTE);
28+ LL_PORT(443, LANDLOCK_ACCESS_NET_CONNECT_TCP);
29+
30+} LL_END
31+
32+int main(void) {
33+
34+ int status = my_sandbox_function("some/path");
35+
36+ if (status != 0) {
37+ // error
38+ }
39+
40+}
41+ */
42+
43+#ifndef __LANDLOC_H__
44+#define __LANDLOC_H__
45+
46+#ifndef __linux__
47+#error "no landlock without linux"
48+#endif
49+
50+#include <unistd.h>
51+#include <linux/landlock.h>
52+#include <sys/syscall.h>
53+#include <sys/prctl.h>
54+#include <fcntl.h>
55+
56+#ifndef O_PATH
57+#define O_PATH 010000000
58+#endif
59+
60+#ifndef LL_PRINTERR
61+#define LL_PRINTERR(fmt, ...) (void)fmt;
62+#else
63+#include <string.h>
64+#include <errno.h>
65+#endif
66+
67+#define LL_FS_ALL (\
68+ LANDLOCK_ACCESS_FS_EXECUTE |\
69+ LANDLOCK_ACCESS_FS_WRITE_FILE |\
70+ LANDLOCK_ACCESS_FS_READ_FILE |\
71+ LANDLOCK_ACCESS_FS_READ_DIR |\
72+ LANDLOCK_ACCESS_FS_REMOVE_DIR |\
73+ LANDLOCK_ACCESS_FS_REMOVE_FILE |\
74+ LANDLOCK_ACCESS_FS_MAKE_CHAR |\
75+ LANDLOCK_ACCESS_FS_MAKE_DIR |\
76+ LANDLOCK_ACCESS_FS_MAKE_REG |\
77+ LANDLOCK_ACCESS_FS_MAKE_SOCK |\
78+ LANDLOCK_ACCESS_FS_MAKE_FIFO |\
79+ LANDLOCK_ACCESS_FS_MAKE_BLOCK |\
80+ LANDLOCK_ACCESS_FS_MAKE_SYM |\
81+ LANDLOCK_ACCESS_FS_REFER |\
82+ LANDLOCK_ACCESS_FS_TRUNCATE |\
83+ LANDLOCK_ACCESS_FS_IOCTL_DEV )
84+
85+#define LL_NET_ALL (\
86+ LANDLOCK_ACCESS_NET_BIND_TCP |\
87+ LANDLOCK_ACCESS_NET_CONNECT_TCP )
88+
89+#define LL_BEGIN(function, ...) int function(__VA_ARGS__) {\
90+ int ll_rule_fd, ll_abi;\
91+ struct landlock_ruleset_attr __rattr = {0};\
92+ struct landlock_path_beneath_attr __pattr = {0};\
93+ struct landlock_net_port_attr __nattr = {0};\
94+ int __err = 0;\
95+ __rattr.handled_access_fs = LL_FS_ALL;\
96+ __rattr.handled_access_net = LL_NET_ALL;\
97+ ll_abi = (int)syscall(SYS_landlock_create_ruleset, NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);\
98+ switch (ll_abi) {\
99+ case -1: return -1;\
100+ case 1: __rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; __attribute__((fallthrough));\
101+ case 2: __rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE; __attribute__((fallthrough));\
102+ case 3: __rattr.handled_access_net &= ~(LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP); __attribute__((fallthrough));\
103+ case 4: __rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;\
104+ default: break;\
105+ }\
106+ ll_rule_fd = (int)syscall(SYS_landlock_create_ruleset, &__rattr, sizeof(struct landlock_ruleset_attr), 0);\
107+ if (-1 == ll_rule_fd) {\
108+ LL_PRINTERR("landlock_create_ruleset: %s", strerror(errno));\
109+ return -1;\
110+ }
111+
112+#define LL_END \
113+ __err = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);\
114+ if (-1 == __err) {\
115+ LL_PRINTERR("set_no_new_privs: %s", strerror(errno));\
116+ goto __close;\
117+ }\
118+ __err = (int)syscall(SYS_landlock_restrict_self, ll_rule_fd, 0);\
119+ if (__err)\
120+ LL_PRINTERR("landlock_restrict_self: %s", strerror(errno));\
121+ __close: close(ll_rule_fd);\
122+ return __err; }
123+
124+#define LL_PATH(p, rules) do {\
125+ const char *__path = (p);\
126+ __pattr.allowed_access = (rules) & __rattr.handled_access_fs;\
127+ __pattr.parent_fd = open(__path, O_PATH | O_CLOEXEC);\
128+ if (-1 == __pattr.parent_fd) {\
129+ LL_PRINTERR("open(%s): %s", __path, strerror(errno));\
130+ __err = -1;\
131+ goto __close;\
132+ }\
133+ __err = (int)syscall(SYS_landlock_add_rule, ll_rule_fd, LANDLOCK_RULE_PATH_BENEATH, &__pattr, 0);\
134+ if (__err) {\
135+ LL_PRINTERR("landlock_add_rule(%s): %s", __path, strerror(errno));\
136+ goto __close;\
137+ }\
138+ close(__pattr.parent_fd);\
139+} while (0)
140+
141+#define LL_PORT(p, rules) do {\
142+ if (ll_abi > 3) {\
143+ unsigned short __port = (p);\
144+ __nattr.allowed_access = (rules);\
145+ __nattr.port = __port;\
146+ __err = (int)syscall(SYS_landlock_add_rule, ll_rule_fd, LANDLOCK_RULE_NET_PORT, &__nattr, 0);\
147+ if (__err) {\
148+ LL_PRINTERR("landlock_add_rule(%u): %s", __port, strerror(errno));\
149+ goto __close;\
150+ }\
151+ }\
152+} while (0)
153+
154+#endif /* __LANDLOC_H__ */
+44,
-129
1@@ -5,12 +5,49 @@
2 #include <unistd.h>
3
4 #if defined (__linux__)
5-# define __USE_GNU
6-# include <linux/landlock.h>
7-# include <sys/syscall.h>
8-# include <sys/prctl.h>
9-# include <stdint.h>
10-# include <fcntl.h>
11+
12+#define LL_PRINTERR(fmt, ...) srv_debug(0, xs_fmt(fmt, __VA_ARGS__))
13+#include "landloc.h"
14+
15+#define LL_R LANDLOCK_ACCESS_FS_READ_FILE
16+#define LL_X LANDLOCK_ACCESS_FS_EXECUTE
17+#define LL_RWCF (LL_R | LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_REFER)
18+#define LL_RWCD (LL_RWCF | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_REMOVE_DIR)
19+#define LL_UNIX (LL_R | LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_MAKE_SOCK)
20+#define LL_CONN LANDLOCK_ACCESS_NET_CONNECT_TCP
21+#define LL_BIND LANDLOCK_ACCESS_NET_BIND_TCP
22+
23+static
24+LL_BEGIN(sbox_enter_linux_, const char* basedir, const char *address, int smail) {
25+
26+ LL_PATH(basedir, LL_RWCD);
27+ LL_PATH("/tmp", LL_RWCD);
28+#ifndef WITHOUT_SHM
29+ LL_PATH("/dev/shm", LL_RWCF);
30+#endif
31+ LL_PATH("/etc/resolv.conf", LL_R );
32+ LL_PATH("/etc/hosts", LL_R );
33+ LL_PATH("/etc/ssl/openssl.cnf", LL_R );
34+ LL_PATH("/etc/ssl/cert.pem", LL_R );
35+ LL_PATH("/usr/share/zoneinfo", LL_R );
36+
37+ if (*address == '/')
38+ LL_PATH(address, LL_UNIX);
39+
40+ if (smail)
41+ LL_PATH("/usr/sbin/sendmail", LL_X);
42+
43+
44+ if (*address != '/') {
45+ LL_PORT(
46+ (unsigned short)xs_number_get(xs_dict_get(srv_config, "port")), LL_BIND);
47+ }
48+
49+ LL_PORT(80, LL_CONN);
50+ LL_PORT(443, LL_CONN);
51+
52+} LL_END
53+
54 #endif
55
56 void sbox_enter(const char *basedir)
57@@ -60,132 +97,10 @@ void sbox_enter(const char *basedir)
58
59 xs_free(p);
60 #elif defined (__linux__)
61- int error, ruleset_fd, abi;
62- struct landlock_ruleset_attr rules = {0};
63- struct landlock_path_beneath_attr path = {0};
64- struct landlock_net_port_attr net = {0};
65-
66- rules.handled_access_fs =
67- LANDLOCK_ACCESS_FS_EXECUTE |
68- LANDLOCK_ACCESS_FS_WRITE_FILE |
69- LANDLOCK_ACCESS_FS_READ_FILE |
70- LANDLOCK_ACCESS_FS_REMOVE_DIR |
71- LANDLOCK_ACCESS_FS_REMOVE_FILE |
72- LANDLOCK_ACCESS_FS_MAKE_CHAR |
73- LANDLOCK_ACCESS_FS_MAKE_DIR |
74- LANDLOCK_ACCESS_FS_MAKE_REG |
75- LANDLOCK_ACCESS_FS_MAKE_SOCK |
76- LANDLOCK_ACCESS_FS_MAKE_FIFO |
77- LANDLOCK_ACCESS_FS_MAKE_BLOCK |
78- LANDLOCK_ACCESS_FS_MAKE_SYM |
79- LANDLOCK_ACCESS_FS_REFER |
80- LANDLOCK_ACCESS_FS_TRUNCATE |
81- LANDLOCK_ACCESS_FS_IOCTL_DEV;
82- rules.handled_access_net =
83- LANDLOCK_ACCESS_NET_BIND_TCP |
84- LANDLOCK_ACCESS_NET_CONNECT_TCP;
85-
86- abi = syscall(SYS_landlock_create_ruleset, NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
87- switch (abi) {
88- case -1:
89- srv_debug(0, xs_dup("Kernel without landlock support"));
90- return;
91- case 1:
92- rules.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
93- __attribute__((fallthrough));
94- case 2:
95- rules.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
96- __attribute__((fallthrough));
97- case 3:
98- rules.handled_access_net &= ~(LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP);
99- __attribute__((fallthrough));
100- case 4:
101- rules.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
102- }
103- srv_debug(1, xs_fmt("lanlock abi: %d", abi));
104-
105- ruleset_fd = syscall(SYS_landlock_create_ruleset, &rules, sizeof(struct landlock_ruleset_attr), 0);
106- if (ruleset_fd == -1) {
107- srv_debug(0, xs_fmt("landlock_create_ruleset failed: %s", strerror(errno)));
108- return;
109- }
110-
111-#define LL_R LANDLOCK_ACCESS_FS_READ_FILE
112-#define LL_X LANDLOCK_ACCESS_FS_EXECUTE
113-#define LL_RWCF (LL_R | LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_REFER)
114-#define LL_RWCD (LL_RWCF | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_REMOVE_DIR)
115-#define LL_UNIX (LL_R | LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_MAKE_SOCK)
116-#define LL_CONN LANDLOCK_ACCESS_NET_CONNECT_TCP
117-#define LL_BIND LANDLOCK_ACCESS_NET_BIND_TCP
118-
119-#define LANDLOCK_PATH(p, r) do {\
120- path.allowed_access = r;\
121- if (abi < 2)\
122- path.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER;\
123- if (abi < 3)\
124- path.allowed_access &= ~LANDLOCK_ACCESS_FS_TRUNCATE;\
125- path.parent_fd = open(p, O_PATH | O_CLOEXEC);\
126- if (path.parent_fd == -1) {\
127- srv_debug(2, xs_fmt("open %s: %s", p, strerror(errno)));\
128- goto close;\
129- }\
130- error = syscall(SYS_landlock_add_rule, ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path, 0); \
131- if (error) {\
132- srv_debug(0, xs_fmt("LANDLOCK_PATH(%s): %s", p, strerror(errno)));\
133- goto close;\
134- }\
135-} while (0)
136-
137-#define LANDLOCK_PORT(p, r) do {\
138- uint16_t _p = p;\
139- net.port = _p;\
140- net.allowed_access = r;\
141- error = syscall(SYS_landlock_add_rule, ruleset_fd, LANDLOCK_RULE_NET_PORT, &net, 0);\
142- if (error) {\
143- srv_debug(0, xs_fmt("LANDLOCK_PORT(%d): %s", _p, strerror(errno)));\
144- goto close;\
145- }\
146-} while (0)
147-
148- LANDLOCK_PATH(basedir, LL_RWCD);
149- LANDLOCK_PATH("/tmp", LL_RWCD);
150-#ifndef WITHOUT_SHM
151- LANDLOCK_PATH("/dev/shm", LL_RWCF);
152-#endif
153- LANDLOCK_PATH("/etc/resolv.conf", LL_R );
154- LANDLOCK_PATH("/etc/hosts", LL_R );
155- LANDLOCK_PATH("/etc/ssl/openssl.cnf", LL_R );
156- LANDLOCK_PATH("/etc/ssl/cert.pem", LL_R );
157- LANDLOCK_PATH("/usr/share/zoneinfo", LL_R );
158-
159- if (*address == '/')
160- LANDLOCK_PATH(address, LL_UNIX);
161-
162- if (smail)
163- LANDLOCK_PATH("/usr/sbin/sendmail", LL_X);
164-
165- if (abi > 3) {
166- if (*address != '/') {
167- LANDLOCK_PORT(
168- (uint16_t)xs_number_get(xs_dict_get(srv_config, "port")), LL_BIND);
169- }
170-
171- LANDLOCK_PORT(80, LL_CONN);
172- LANDLOCK_PORT(443, LL_CONN);
173- }
174
175- if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
176- srv_debug(0, xs_fmt("prctl SET_NO_NEW_PRIVS: %s", strerror(errno)));
177- goto close;
178- }
179-
180- if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0))
181- srv_debug(0, xs_fmt("landlock_restrict_self: %s", strerror(errno)));
182+ sbox_enter_linux_(basedir, address, smail);
183
184 srv_log(xs_dup("landlocked"));
185
186-close:
187- close(ruleset_fd);
188-
189 #endif
190 }