commit 4985b3e

default  ·  2025-01-05 12:17:03 +0000 UTC
parents a69968f, fced3aa
Merge branch 'master' of grunfink-codeberg:grunfink/snac2
5 files changed,  +358, -39
M data.c
M snac.h
+4, -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$(PREFIX)/lib *.o -lcurl -lcrypto $(LDFLAGS) -pthread -o $@
 9 
10@@ -36,6 +36,9 @@ 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 \
16+ landloc.h snac.h
17 data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \
18  xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h snac.h \
19  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());
+238, -0
  1@@ -0,0 +1,238 @@
  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+ * Repository: https://git.sr.ht/~shtrophic/landloc.h
 23+ */
 24+
 25+/**
 26+ * Usage:
 27+ *
 28+ * Define a sandboxing function using the LL_BEGIN(...) and LL_END macros.
 29+ * the arguments of LL_BEGIN are the function's signature.
 30+ * Between those macros, implement your sandbox using LL_PATH() and LL_PORT() macros.
 31+ * Calling LL_PATH() and LL_PORT() anywhere else will not work.
 32+ * You may prepend `static` before LL_BEGIN to make the function static.
 33+ * You need (should) wrap your sandboxing code in another set of braces:
 34+ *
 35+LL_BEGIN(my_sandbox_function, const char *rw_path) {
 36+
 37+    LL_PATH(rw_path, LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_EXECUTE);
 38+    LL_PORT(443, LANDLOCK_ACCESS_NET_CONNECT_TCP);
 39+
 40+} LL_END
 41+
 42+ *
 43+ * Then, call it in your application's code.
 44+ *
 45+
 46+int main(void) {
 47+
 48+    int status = my_sandbox_function("some/path");
 49+
 50+    if (status != 0) {
 51+        // error
 52+    }
 53+
 54+}
 55+
 56+ *
 57+ * You may define LL_PRINTERR(fmt, ...) before including this header to enable debug output:
 58+ *
 59+
 60+#define LL_PRINTERR(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__)
 61+#include "landloc.h"
 62+
 63+ */
 64+
 65+#ifndef __LANDLOC_H__
 66+#define __LANDLOC_H__
 67+
 68+#ifndef __linux__
 69+#   error "no landlock without linux"
 70+#endif
 71+
 72+#include <linux/version.h>
 73+
 74+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
 75+#   error "no landlock on kernels older than 5.13.0"
 76+#endif
 77+
 78+#include <unistd.h>
 79+#include <linux/landlock.h>
 80+#include <sys/syscall.h>
 81+#include <sys/prctl.h>
 82+#include <fcntl.h>
 83+
 84+#ifndef O_PATH
 85+#   define O_PATH		010000000
 86+#endif
 87+
 88+#ifndef LL_PRINTERR
 89+#   define LL_PRINTERR(fmt, ...) (void)fmt;
 90+#else
 91+#   include <string.h>
 92+#   include <errno.h>
 93+#endif
 94+
 95+#ifdef LANDLOCK_ACCESS_FS_REFER
 96+#   define LANDLOCK_ACCESS_FS_REFER_COMPAT LANDLOCK_ACCESS_FS_REFER
 97+#   define __LL_SWITCH_FS_REFER __rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER_COMPAT
 98+#else
 99+#   define LANDLOCK_ACCESS_FS_REFER_COMPAT 0
100+#   define __LL_SWITCH_FS_REFER (void)0
101+#endif
102+
103+#ifdef LANDLOCK_ACCESS_FS_TRUNCATE
104+#   define LANDLOCK_ACCESS_FS_TRUNCATE_COMPAT LANDLOCK_ACCESS_FS_TRUNCATE
105+#   define __LL_SWITCH_FS_TRUNCATE __rattr.handled_access_fs  &= ~LANDLOCK_ACCESS_FS_TRUNCATE_COMPAT
106+#else
107+#   define LANDLOCK_ACCESS_FS_TRUNCATE_COMPAT 0
108+#   define __LL_SWITCH_FS_TRUNCATE (void)0
109+#endif
110+
111+#ifdef LANDLOCK_ACCESS_FS_IOCTL_DEV
112+#   define LANDLOCK_ACCESS_FS_IOCTL_DEV_COMPAT LANDLOCK_ACCESS_FS_IOCTL_DEV
113+#   define __LL_SWITCH_FS_IOCTL_DEV __rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV_COMPAT
114+#else
115+#   define LANDLOCK_ACCESS_FS_IOCTL_DEV_COMPAT 0
116+#   define __LL_SWITCH_FS_IOCTL_DEV (void)0
117+#endif
118+
119+#define LL_FS_ALL                           (\
120+    LANDLOCK_ACCESS_FS_EXECUTE              |\
121+    LANDLOCK_ACCESS_FS_WRITE_FILE           |\
122+    LANDLOCK_ACCESS_FS_READ_FILE            |\
123+    LANDLOCK_ACCESS_FS_READ_DIR             |\
124+    LANDLOCK_ACCESS_FS_REMOVE_DIR           |\
125+    LANDLOCK_ACCESS_FS_REMOVE_FILE          |\
126+    LANDLOCK_ACCESS_FS_MAKE_CHAR            |\
127+    LANDLOCK_ACCESS_FS_MAKE_DIR             |\
128+    LANDLOCK_ACCESS_FS_MAKE_REG             |\
129+    LANDLOCK_ACCESS_FS_MAKE_SOCK            |\
130+    LANDLOCK_ACCESS_FS_MAKE_FIFO            |\
131+    LANDLOCK_ACCESS_FS_MAKE_BLOCK           |\
132+    LANDLOCK_ACCESS_FS_MAKE_SYM             |\
133+    LANDLOCK_ACCESS_FS_REFER_COMPAT         |\
134+    LANDLOCK_ACCESS_FS_TRUNCATE_COMPAT      |\
135+    LANDLOCK_ACCESS_FS_IOCTL_DEV_COMPAT     )
136+
137+#if defined(LANDLOCK_ACCESS_NET_BIND_TCP) && defined(LANDLOCK_ACCESS_NET_CONNECT_TCP)
138+#   define LL_HAVE_NET 1
139+
140+#   define LANDLOCK_ACCESS_NET_BIND_TCP_COMPAT LANDLOCK_ACCESS_NET_BIND_TCP
141+#   define LANDLOCK_ACCESS_NET_CONNECT_TCP_COMPAT LANDLOCK_ACCESS_NET_CONNECT_TCP
142+
143+#   define LL_NET_ALL (LANDLOCK_ACCESS_NET_BIND_TCP_COMPAT | LANDLOCK_ACCESS_NET_CONNECT_TCP_COMPAT)
144+#   define __LL_DECLARE_NET struct landlock_net_port_attr __nattr = {0}
145+#   define __LL_INIT_NET __rattr.handled_access_net = LL_NET_ALL
146+#   define __LL_SWITCH_NET do { __rattr.handled_access_net &= ~(LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP); } while (0)
147+#else
148+#   define LL_HAVE_NET 0
149+
150+#   define LANDLOCK_ACCESS_NET_BIND_TCP_COMPAT 0
151+#   define LANDLOCK_ACCESS_NET_CONNECT_TCP_COMPAT 0
152+
153+#   define LL_NET_ALL 0
154+#   define __LL_DECLARE_NET (void)0
155+#   define __LL_INIT_NET (void)0
156+#   define __LL_SWITCH_NET (void)0
157+#endif
158+
159+#define LL_BEGIN(function, ...) int function(__VA_ARGS__) {\
160+    int ll_rule_fd, ll_abi;\
161+    struct landlock_ruleset_attr      __rattr = {0};\
162+    struct landlock_path_beneath_attr __pattr = {0};\
163+    __LL_DECLARE_NET;\
164+    int __err = 0;\
165+    __rattr.handled_access_fs  = LL_FS_ALL;\
166+    __LL_INIT_NET;\
167+    ll_abi = (int)syscall(SYS_landlock_create_ruleset, NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);\
168+    switch (ll_abi) {\
169+    case -1: return -1;\
170+    case  1: __LL_SWITCH_FS_REFER; __attribute__((fallthrough));\
171+    case  2: __LL_SWITCH_FS_TRUNCATE; __attribute__((fallthrough));\
172+    case  3: __LL_SWITCH_NET; __attribute__((fallthrough));\
173+    case  4: __LL_SWITCH_FS_IOCTL_DEV;\
174+    default: break;\
175+    }\
176+    ll_rule_fd = (int)syscall(SYS_landlock_create_ruleset, &__rattr, sizeof(struct landlock_ruleset_attr), 0);\
177+    if (-1 == ll_rule_fd) {\
178+        LL_PRINTERR("landlock_create_ruleset: %s", strerror(errno));\
179+        return -1;\
180+    }
181+
182+#define LL_END \
183+    __err = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);\
184+    if (-1 == __err) {\
185+        LL_PRINTERR("set_no_new_privs: %s", strerror(errno));\
186+        goto __close;\
187+    }\
188+    __err = (int)syscall(SYS_landlock_restrict_self, ll_rule_fd, 0);\
189+    if (__err)\
190+        LL_PRINTERR("landlock_restrict_self: %s", strerror(errno));\
191+    __close: close(ll_rule_fd);\
192+    return __err; }
193+
194+#define LL_PATH(p, rules) do {\
195+    const char *__path = (p);\
196+    __pattr.allowed_access = (rules) & __rattr.handled_access_fs;\
197+    if (__pattr.allowed_access != 0) {\
198+        __pattr.parent_fd = open(__path, O_PATH | O_CLOEXEC);\
199+        if (-1 == __pattr.parent_fd) {\
200+            LL_PRINTERR("open(%s): %s", __path, strerror(errno));\
201+            __err = -1;\
202+            goto __close;\
203+        }\
204+        __err = (int)syscall(SYS_landlock_add_rule, ll_rule_fd, LANDLOCK_RULE_PATH_BENEATH, &__pattr, 0);\
205+        if (__err) {\
206+            LL_PRINTERR("landlock_add_rule(%s): %s", __path, strerror(errno));\
207+            goto __close;\
208+        }\
209+        close(__pattr.parent_fd);\
210+    }\
211+} while (0)
212+
213+#if LL_HAVE_NET
214+
215+#define LL_PORT(p, rules) do {\
216+    unsigned short __port = (p);\
217+    __nattr.allowed_access = (rules);\
218+    if (ll_abi > 3 && __nattr.allowed_access != 0) {\
219+        __nattr.port = __port;\
220+        __err = (int)syscall(SYS_landlock_add_rule, ll_rule_fd, LANDLOCK_RULE_NET_PORT, &__nattr, 0);\
221+        if (__err) {\
222+            LL_PRINTERR("landlock_add_rule(%u): %s", __port, strerror(errno));\
223+            goto __close;\
224+        }\
225+    }\
226+} while (0)
227+
228+#else
229+
230+#define LL_PORT(p, rules) do {\
231+    unsigned short __port = (p);\
232+    __u64 __rules = (rules);\
233+    (void)__port;\
234+    (void)__rules;\
235+} while (0)
236+
237+#endif /* LL_HAVE_NET */
238+
239+#endif /* __LANDLOC_H__ */
+113, -0
  1@@ -0,0 +1,113 @@
  2+#include "xs.h"
  3+
  4+#include "snac.h"
  5+
  6+#include <unistd.h>
  7+
  8+#if defined (__linux__)
  9+
 10+#define LL_PRINTERR(fmt, ...) srv_debug(0, xs_fmt(fmt, __VA_ARGS__))
 11+#include "landloc.h"
 12+
 13+static
 14+LL_BEGIN(sbox_enter_linux_, const char* basedir, const char *address, int smail) {
 15+
 16+    const unsigned long long
 17+        rd = LANDLOCK_ACCESS_FS_READ_DIR,
 18+        rf = LANDLOCK_ACCESS_FS_READ_FILE,
 19+        w  = LANDLOCK_ACCESS_FS_WRITE_FILE      |
 20+             LANDLOCK_ACCESS_FS_TRUNCATE_COMPAT,
 21+        c  = LANDLOCK_ACCESS_FS_MAKE_DIR        |
 22+             LANDLOCK_ACCESS_FS_MAKE_REG        |
 23+             LANDLOCK_ACCESS_FS_TRUNCATE_COMPAT |
 24+             LANDLOCK_ACCESS_FS_MAKE_SYM        |
 25+             LANDLOCK_ACCESS_FS_REMOVE_DIR      |
 26+             LANDLOCK_ACCESS_FS_REMOVE_FILE     |
 27+             LANDLOCK_ACCESS_FS_REFER_COMPAT,
 28+        s  = LANDLOCK_ACCESS_FS_MAKE_SOCK,
 29+        x  = LANDLOCK_ACCESS_FS_EXECUTE;
 30+
 31+    LL_PATH(basedir,                rf|rd|w|c);
 32+    LL_PATH("/tmp",                 rf|rd|w|c);
 33+#ifndef WITHOUT_SHM
 34+    LL_PATH("/dev/shm",             rf|w|c   );
 35+#endif
 36+    LL_PATH("/etc/resolv.conf",     rf       );
 37+    LL_PATH("/etc/hosts",           rf       );
 38+    LL_PATH("/etc/ssl/openssl.cnf", rf       );
 39+    LL_PATH("/etc/ssl/cert.pem",    rf       );
 40+    LL_PATH("/usr/share/zoneinfo",  rf       );
 41+
 42+    if (*address == '/')
 43+        LL_PATH(address, s);
 44+
 45+    if (smail)
 46+        LL_PATH("/usr/sbin/sendmail", x);
 47+
 48+    if (*address != '/') {
 49+        unsigned short listen_port = xs_number_get(xs_dict_get(srv_config, "port"));
 50+        LL_PORT(listen_port, LANDLOCK_ACCESS_NET_BIND_TCP_COMPAT);
 51+    }
 52+
 53+    LL_PORT(80,  LANDLOCK_ACCESS_NET_CONNECT_TCP_COMPAT);
 54+    LL_PORT(443, LANDLOCK_ACCESS_NET_CONNECT_TCP_COMPAT);
 55+
 56+} LL_END
 57+
 58+#endif
 59+
 60+void sbox_enter(const char *basedir)
 61+{
 62+    if (xs_is_true(xs_dict_get(srv_config, "disable_openbsd_security"))) {
 63+        srv_log(xs_dup("disable_openbsd_security is deprecated. Use disable_sandbox instead."));
 64+        return;
 65+    }
 66+    if (xs_is_true(xs_dict_get(srv_config, "disable_sandbox"))) {
 67+        srv_debug(0, xs_dup("Sandbox disabled by admin"));
 68+        return;
 69+    }
 70+
 71+    const char *address = xs_dict_get(srv_config, "address");
 72+
 73+    int smail = !xs_is_true(xs_dict_get(srv_config, "disable_email_notifications"));
 74+
 75+#if defined (__OpenBSD__)
 76+    srv_debug(1, xs_fmt("Calling unveil()"));
 77+    unveil(basedir,                "rwc");
 78+    unveil("/tmp",                 "rwc");
 79+    unveil("/etc/resolv.conf",     "r");
 80+    unveil("/etc/hosts",           "r");
 81+    unveil("/etc/ssl/openssl.cnf", "r");
 82+    unveil("/etc/ssl/cert.pem",    "r");
 83+    unveil("/usr/share/zoneinfo",  "r");
 84+
 85+    if (smail)
 86+        unveil("/usr/sbin/sendmail",   "x");
 87+
 88+    if (*address == '/')
 89+        unveil(address, "rwc");
 90+
 91+    unveil(NULL,                   NULL);
 92+
 93+    srv_debug(1, xs_fmt("Calling pledge()"));
 94+
 95+    xs *p = xs_str_new("stdio rpath wpath cpath flock inet proc dns fattr");
 96+
 97+    if (smail)
 98+        p = xs_str_cat(p, " exec");
 99+
100+    if (*address == '/')
101+        p = xs_str_cat(p, " unix");
102+
103+    pledge(p, NULL);
104+
105+    xs_free(p);
106+#elif defined (__linux__)
107+    
108+    if (sbox_enter_linux_(basedir, address, smail) == 0)
109+        srv_log(xs_dup("landlocked"));
110+    else
111+        srv_log(xs_dup("landlocking failed"));
112+
113+#endif
114+}
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);