commit 75f6159
shtrophic
·
2024-11-12 20:01:09 +0000 UTC
parent f221302
sandboxing port to linux via landlock
M
Makefile
+3,
-1
1@@ -4,7 +4,7 @@ CFLAGS?=-g -Wall -Wextra -pedantic
2
3 all: snac
4
5-snac: snac.o main.o data.o http.o httpd.o webfinger.o \
6+snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \
7 activitypub.o html.o utils.o format.o upgrade.o mastoapi.o
8 $(CC) $(CFLAGS) -L/usr/local/lib *.o -lcurl -lcrypto $(LDFLAGS) -pthread -o $@
9
10@@ -36,6 +36,8 @@ uninstall:
11 activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \
12 xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h snac.h \
13 http_codes.h
14+sandbox.o: sandbox.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h \
15+ xs_glob.h xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h snac.h
16 data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \
17 xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h snac.h \
18 http_codes.h
M
data.c
+1,
-38
1@@ -115,44 +115,7 @@ int srv_open(const char *basedir, int auto_upgrade)
2 #define st_mtim st_mtimespec
3 #endif
4
5-#ifdef __OpenBSD__
6- if (xs_is_true(xs_dict_get(srv_config, "disable_openbsd_security"))) {
7- srv_debug(1, xs_dup("OpenBSD security disabled by admin"));
8- }
9- else {
10- int smail = !xs_is_true(xs_dict_get(srv_config, "disable_email_notifications"));
11- const char *address = xs_dict_get(srv_config, "address");
12-
13- srv_debug(1, xs_fmt("Calling unveil()"));
14- unveil(basedir, "rwc");
15- unveil("/tmp", "rwc");
16- unveil("/etc/resolv.conf", "r");
17- unveil("/etc/hosts", "r");
18- unveil("/etc/ssl/openssl.cnf", "r");
19- unveil("/etc/ssl/cert.pem", "r");
20- unveil("/usr/share/zoneinfo", "r");
21-
22- if (smail)
23- unveil("/usr/sbin/sendmail", "x");
24-
25- if (*address == '/')
26- unveil(address, "rwc");
27-
28- unveil(NULL, NULL);
29-
30- srv_debug(1, xs_fmt("Calling pledge()"));
31-
32- xs *p = xs_str_new("stdio rpath wpath cpath flock inet proc dns fattr");
33-
34- if (smail)
35- p = xs_str_cat(p, " exec");
36-
37- if (*address == '/')
38- p = xs_str_cat(p, " unix");
39-
40- pledge(p, NULL);
41- }
42-#endif /* __OpenBSD__ */
43+ sbox_enter(srv_basedir);
44
45 /* read (and drop) emojis.json, possibly creating it */
46 xs_free(emojis());
+184,
-0
1@@ -0,0 +1,184 @@
2+#include "xs.h"
3+
4+#include "snac.h"
5+
6+#include <unistd.h>
7+
8+#if defined (__linux__)
9+# define __USE_GNU
10+# include <linux/landlock.h>
11+# include <linux/prctl.h>
12+# include <sys/syscall.h>
13+# include <sys/prctl.h>
14+# include <fcntl.h>
15+# include <arpa/inet.h>
16+#endif
17+
18+void sbox_enter(const char *basedir)
19+{
20+ if (xs_is_true(xs_dict_get(srv_config, "disable_openbsd_security"))) {
21+ srv_log(xs_dup("disable_openbsd_security is deprecated. Use disable_sandbox instead."));
22+ return;
23+ }
24+ if (xs_is_true(xs_dict_get(srv_config, "disable_sandbox"))) {
25+ srv_debug(0, xs_dup("Sandbox disabled by admin"));
26+ return;
27+ }
28+
29+ const char *address = xs_dict_get(srv_config, "address");
30+
31+#if defined (__OpenBSD__)
32+ int smail = !xs_is_true(xs_dict_get(srv_config, "disable_email_notifications"));
33+
34+ srv_debug(1, xs_fmt("Calling unveil()"));
35+ unveil(basedir, "rwc");
36+ unveil("/tmp", "rwc");
37+ unveil("/etc/resolv.conf", "r");
38+ unveil("/etc/hosts", "r");
39+ unveil("/etc/ssl/openssl.cnf", "r");
40+ unveil("/etc/ssl/cert.pem", "r");
41+ unveil("/usr/share/zoneinfo", "r");
42+
43+ if (smail)
44+ unveil("/usr/sbin/sendmail", "x");
45+
46+ if (*address == '/')
47+ unveil(address, "rwc");
48+
49+ unveil(NULL, NULL);
50+
51+ srv_debug(1, xs_fmt("Calling pledge()"));
52+
53+ xs *p = xs_str_new("stdio rpath wpath cpath flock inet proc dns fattr");
54+
55+ if (smail)
56+ p = xs_str_cat(p, " exec");
57+
58+ if (*address == '/')
59+ p = xs_str_cat(p, " unix");
60+
61+ pledge(p, NULL);
62+
63+ xs_free(p);
64+#elif defined (__linux__)
65+ int error, ruleset_fd, abi;
66+ struct landlock_ruleset_attr rules = {0};
67+ struct landlock_path_beneath_attr path = {0};
68+ struct landlock_net_port_attr net = {0};
69+
70+ rules.handled_access_fs =
71+ LANDLOCK_ACCESS_FS_EXECUTE |
72+ LANDLOCK_ACCESS_FS_WRITE_FILE |
73+ LANDLOCK_ACCESS_FS_READ_FILE |
74+ LANDLOCK_ACCESS_FS_REMOVE_DIR |
75+ LANDLOCK_ACCESS_FS_REMOVE_FILE |
76+ LANDLOCK_ACCESS_FS_MAKE_CHAR |
77+ LANDLOCK_ACCESS_FS_MAKE_DIR |
78+ LANDLOCK_ACCESS_FS_MAKE_REG |
79+ LANDLOCK_ACCESS_FS_MAKE_SOCK |
80+ LANDLOCK_ACCESS_FS_MAKE_FIFO |
81+ LANDLOCK_ACCESS_FS_MAKE_BLOCK |
82+ LANDLOCK_ACCESS_FS_MAKE_SYM |
83+ LANDLOCK_ACCESS_FS_REFER |
84+ LANDLOCK_ACCESS_FS_TRUNCATE |
85+ LANDLOCK_ACCESS_FS_IOCTL_DEV;
86+ rules.handled_access_net =
87+ LANDLOCK_ACCESS_NET_BIND_TCP |
88+ LANDLOCK_ACCESS_NET_CONNECT_TCP;
89+
90+ abi = syscall(SYS_landlock_create_ruleset, NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
91+ switch (abi) {
92+ case -1:
93+ srv_debug(0, xs_dup("Kernel without landlock support"));
94+ return;
95+ case 1:
96+ rules.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
97+ __attribute__((fallthrough));
98+ case 2:
99+ rules.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
100+ __attribute__((fallthrough));
101+ case 3:
102+ rules.handled_access_net &= ~(LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP);
103+ __attribute__((fallthrough));
104+ case 4:
105+ rules.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
106+ }
107+ srv_debug(1, xs_fmt("lanlock abi: %d", abi));
108+
109+ ruleset_fd = syscall(SYS_landlock_create_ruleset, &rules, sizeof(struct landlock_ruleset_attr), 0);
110+ if (ruleset_fd == -1) {
111+ srv_debug(0, xs_fmt("landlock_create_ruleset failed: %s", strerror(errno)));
112+ return;
113+ }
114+
115+#define LL_R LANDLOCK_ACCESS_FS_READ_FILE
116+#define LL_X LANDLOCK_ACCESS_FS_EXECUTE
117+#define LL_RWC (LL_R | LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_TRUNCATE)
118+#define LL_UNX (LL_R | LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_MAKE_SOCK)
119+#define LL_CON LANDLOCK_ACCESS_NET_CONNECT_TCP
120+#define LL_BND LANDLOCK_ACCESS_NET_BIND_TCP
121+
122+#define LANDLOCK_PATH(p, r) do {\
123+ path.allowed_access = r;\
124+ if (abi < 3)\
125+ path.allowed_access &= ~LANDLOCK_ACCESS_FS_TRUNCATE;\
126+ path.parent_fd = open(p, O_PATH | O_CLOEXEC);\
127+ if (path.parent_fd == -1) {\
128+ srv_debug(2, xs_fmt("open %s: %s", p, strerror(errno)));\
129+ goto close;\
130+ }\
131+ error = syscall(SYS_landlock_add_rule, ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path, 0); \
132+ if (error) {\
133+ srv_debug(0, xs_fmt("LANDLOCK_PATH(%s): %s", p, strerror(errno)));\
134+ goto close;\
135+ }\
136+} while (0)
137+
138+#define LANDLOCK_PORT(p, r) do {\
139+ uint16_t _p = p;\
140+ net.port = _p;\
141+ net.allowed_access = r;\
142+ error = syscall(SYS_landlock_add_rule, ruleset_fd, LANDLOCK_RULE_NET_PORT, &net, 0);\
143+ if (error) {\
144+ srv_debug(0, xs_fmt("LANDLOCK_PORT(%d): %s", _p, strerror(errno)));\
145+ goto close;\
146+ }\
147+} while (0)
148+
149+ LANDLOCK_PATH(basedir, LL_RWC);
150+ LANDLOCK_PATH("/tmp", LL_RWC);
151+ LANDLOCK_PATH("/dev/shm", LL_RWC);
152+ LANDLOCK_PATH("/etc/resolv.conf", LL_R );
153+ LANDLOCK_PATH("/etc/hosts", LL_R );
154+ LANDLOCK_PATH("/etc/ssl/openssl.cnf", LL_R );
155+ LANDLOCK_PATH("/etc/ssl/cert.pem", LL_R );
156+ LANDLOCK_PATH("/usr/share/zoneinfo", LL_R );
157+
158+ if (*address == '/')
159+ LANDLOCK_PATH(address, LL_UNX);
160+
161+ if (abi > 3) {
162+ if (*address != '/') {
163+ LANDLOCK_PORT(
164+ (uint16_t)xs_number_get(xs_dict_get(srv_config, "port")), LL_BND);
165+ }
166+
167+ LANDLOCK_PORT(80, LL_CON);
168+ LANDLOCK_PORT(443, LL_CON);
169+ }
170+
171+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
172+ srv_debug(0, xs_fmt("prctl SET_NO_NEW_PRIVS: %s", strerror(errno)));
173+ goto close;
174+ }
175+
176+ if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0))
177+ srv_debug(0, xs_fmt("landlock_restrict_self: %s", strerror(errno)));
178+
179+ srv_log(xs_dup("landlocked"));
180+
181+close:
182+ close(ruleset_fd);
183+
184+#endif
185+}
M
snac.h
+2,
-0
1@@ -75,6 +75,8 @@ void snac_log(snac *user, xs_str *str);
2 int srv_open(const char *basedir, int auto_upgrade);
3 void srv_free(void);
4
5+void sbox_enter(const char *basedir);
6+
7 int user_open(snac *snac, const char *uid);
8 void user_free(snac *snac);
9 xs_list *user_list(void);