snac2

Fork of https://codeberg.org/grunfink/snac2
git clone https://git.inz.fi/snac2
Log | Files | Refs | README | LICENSE

commit e52b4bf39b7236b2a89e34aaf5c54db2e2b285d8
parent 7e743e8918e0a4d058217ec33ab745b1f1a97335
Author: shtrophic <christoph@liebender.dev>
Date:   Thu,  5 Dec 2024 17:24:04 +0100

import landloc.h

Diffstat:
MMakefile | 3++-
Alandloc.h | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msandbox.c | 173++++++++++++++++++++-----------------------------------------------------------
3 files changed, 199 insertions(+), 130 deletions(-)

diff --git a/Makefile b/Makefile @@ -37,7 +37,8 @@ activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h snac.h \ http_codes.h sandbox.o: sandbox.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h \ - xs_glob.h xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h snac.h + xs_glob.h xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h \ + landloc.h snac.h data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h snac.h \ http_codes.h diff --git a/landloc.h b/landloc.h @@ -0,0 +1,153 @@ +/** + * Zero-Clause BSD + * =============== + * + * Copyright 2024 shtrophic <christoph@liebender.dev> + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/** + * Usage example: + * + +LL_BEGIN(my_sandbox_function, const char *rw_path) { + + LL_PATH(rw_path, LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_EXECUTE); + LL_PORT(443, LANDLOCK_ACCESS_NET_CONNECT_TCP); + +} LL_END + +int main(void) { + + int status = my_sandbox_function("some/path"); + + if (status != 0) { + // error + } + +} + */ + +#ifndef __LANDLOC_H__ +#define __LANDLOC_H__ + +#ifndef __linux__ +#error "no landlock without linux" +#endif + +#include <unistd.h> +#include <linux/landlock.h> +#include <sys/syscall.h> +#include <sys/prctl.h> +#include <fcntl.h> + +#ifndef O_PATH +#define O_PATH 010000000 +#endif + +#ifndef LL_PRINTERR +#define LL_PRINTERR(fmt, ...) (void)fmt; +#else +#include <string.h> +#include <errno.h> +#endif + +#define LL_FS_ALL (\ + LANDLOCK_ACCESS_FS_EXECUTE |\ + LANDLOCK_ACCESS_FS_WRITE_FILE |\ + LANDLOCK_ACCESS_FS_READ_FILE |\ + LANDLOCK_ACCESS_FS_READ_DIR |\ + LANDLOCK_ACCESS_FS_REMOVE_DIR |\ + LANDLOCK_ACCESS_FS_REMOVE_FILE |\ + LANDLOCK_ACCESS_FS_MAKE_CHAR |\ + LANDLOCK_ACCESS_FS_MAKE_DIR |\ + LANDLOCK_ACCESS_FS_MAKE_REG |\ + LANDLOCK_ACCESS_FS_MAKE_SOCK |\ + LANDLOCK_ACCESS_FS_MAKE_FIFO |\ + LANDLOCK_ACCESS_FS_MAKE_BLOCK |\ + LANDLOCK_ACCESS_FS_MAKE_SYM |\ + LANDLOCK_ACCESS_FS_REFER |\ + LANDLOCK_ACCESS_FS_TRUNCATE |\ + LANDLOCK_ACCESS_FS_IOCTL_DEV ) + +#define LL_NET_ALL (\ + LANDLOCK_ACCESS_NET_BIND_TCP |\ + LANDLOCK_ACCESS_NET_CONNECT_TCP ) + +#define LL_BEGIN(function, ...) int function(__VA_ARGS__) {\ + int ll_rule_fd, ll_abi;\ + struct landlock_ruleset_attr __rattr = {0};\ + struct landlock_path_beneath_attr __pattr = {0};\ + struct landlock_net_port_attr __nattr = {0};\ + int __err = 0;\ + __rattr.handled_access_fs = LL_FS_ALL;\ + __rattr.handled_access_net = LL_NET_ALL;\ + ll_abi = (int)syscall(SYS_landlock_create_ruleset, NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);\ + switch (ll_abi) {\ + case -1: return -1;\ + case 1: __rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; __attribute__((fallthrough));\ + case 2: __rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE; __attribute__((fallthrough));\ + case 3: __rattr.handled_access_net &= ~(LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP); __attribute__((fallthrough));\ + case 4: __rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;\ + default: break;\ + }\ + ll_rule_fd = (int)syscall(SYS_landlock_create_ruleset, &__rattr, sizeof(struct landlock_ruleset_attr), 0);\ + if (-1 == ll_rule_fd) {\ + LL_PRINTERR("landlock_create_ruleset: %s", strerror(errno));\ + return -1;\ + } + +#define LL_END \ + __err = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);\ + if (-1 == __err) {\ + LL_PRINTERR("set_no_new_privs: %s", strerror(errno));\ + goto __close;\ + }\ + __err = (int)syscall(SYS_landlock_restrict_self, ll_rule_fd, 0);\ + if (__err)\ + LL_PRINTERR("landlock_restrict_self: %s", strerror(errno));\ + __close: close(ll_rule_fd);\ + return __err; } + +#define LL_PATH(p, rules) do {\ + const char *__path = (p);\ + __pattr.allowed_access = (rules) & __rattr.handled_access_fs;\ + __pattr.parent_fd = open(__path, O_PATH | O_CLOEXEC);\ + if (-1 == __pattr.parent_fd) {\ + LL_PRINTERR("open(%s): %s", __path, strerror(errno));\ + __err = -1;\ + goto __close;\ + }\ + __err = (int)syscall(SYS_landlock_add_rule, ll_rule_fd, LANDLOCK_RULE_PATH_BENEATH, &__pattr, 0);\ + if (__err) {\ + LL_PRINTERR("landlock_add_rule(%s): %s", __path, strerror(errno));\ + goto __close;\ + }\ + close(__pattr.parent_fd);\ +} while (0) + +#define LL_PORT(p, rules) do {\ + if (ll_abi > 3) {\ + unsigned short __port = (p);\ + __nattr.allowed_access = (rules);\ + __nattr.port = __port;\ + __err = (int)syscall(SYS_landlock_add_rule, ll_rule_fd, LANDLOCK_RULE_NET_PORT, &__nattr, 0);\ + if (__err) {\ + LL_PRINTERR("landlock_add_rule(%u): %s", __port, strerror(errno));\ + goto __close;\ + }\ + }\ +} while (0) + +#endif /* __LANDLOC_H__ */ diff --git a/sandbox.c b/sandbox.c @@ -5,12 +5,49 @@ #include <unistd.h> #if defined (__linux__) -# define __USE_GNU -# include <linux/landlock.h> -# include <sys/syscall.h> -# include <sys/prctl.h> -# include <stdint.h> -# include <fcntl.h> + +#define LL_PRINTERR(fmt, ...) srv_debug(0, xs_fmt(fmt, __VA_ARGS__)) +#include "landloc.h" + +#define LL_R LANDLOCK_ACCESS_FS_READ_FILE +#define LL_X LANDLOCK_ACCESS_FS_EXECUTE +#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) +#define LL_RWCD (LL_RWCF | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_REMOVE_DIR) +#define LL_UNIX (LL_R | LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_MAKE_SOCK) +#define LL_CONN LANDLOCK_ACCESS_NET_CONNECT_TCP +#define LL_BIND LANDLOCK_ACCESS_NET_BIND_TCP + +static +LL_BEGIN(sbox_enter_linux_, const char* basedir, const char *address, int smail) { + + LL_PATH(basedir, LL_RWCD); + LL_PATH("/tmp", LL_RWCD); +#ifndef WITHOUT_SHM + LL_PATH("/dev/shm", LL_RWCF); +#endif + LL_PATH("/etc/resolv.conf", LL_R ); + LL_PATH("/etc/hosts", LL_R ); + LL_PATH("/etc/ssl/openssl.cnf", LL_R ); + LL_PATH("/etc/ssl/cert.pem", LL_R ); + LL_PATH("/usr/share/zoneinfo", LL_R ); + + if (*address == '/') + LL_PATH(address, LL_UNIX); + + if (smail) + LL_PATH("/usr/sbin/sendmail", LL_X); + + + if (*address != '/') { + LL_PORT( + (unsigned short)xs_number_get(xs_dict_get(srv_config, "port")), LL_BIND); + } + + LL_PORT(80, LL_CONN); + LL_PORT(443, LL_CONN); + +} LL_END + #endif void sbox_enter(const char *basedir) @@ -60,132 +97,10 @@ void sbox_enter(const char *basedir) xs_free(p); #elif defined (__linux__) - int error, ruleset_fd, abi; - struct landlock_ruleset_attr rules = {0}; - struct landlock_path_beneath_attr path = {0}; - struct landlock_net_port_attr net = {0}; - - rules.handled_access_fs = - LANDLOCK_ACCESS_FS_EXECUTE | - LANDLOCK_ACCESS_FS_WRITE_FILE | - LANDLOCK_ACCESS_FS_READ_FILE | - LANDLOCK_ACCESS_FS_REMOVE_DIR | - LANDLOCK_ACCESS_FS_REMOVE_FILE | - LANDLOCK_ACCESS_FS_MAKE_CHAR | - LANDLOCK_ACCESS_FS_MAKE_DIR | - LANDLOCK_ACCESS_FS_MAKE_REG | - LANDLOCK_ACCESS_FS_MAKE_SOCK | - LANDLOCK_ACCESS_FS_MAKE_FIFO | - LANDLOCK_ACCESS_FS_MAKE_BLOCK | - LANDLOCK_ACCESS_FS_MAKE_SYM | - LANDLOCK_ACCESS_FS_REFER | - LANDLOCK_ACCESS_FS_TRUNCATE | - LANDLOCK_ACCESS_FS_IOCTL_DEV; - rules.handled_access_net = - LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP; - - abi = syscall(SYS_landlock_create_ruleset, NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); - switch (abi) { - case -1: - srv_debug(0, xs_dup("Kernel without landlock support")); - return; - case 1: - rules.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; - __attribute__((fallthrough)); - case 2: - rules.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE; - __attribute__((fallthrough)); - case 3: - rules.handled_access_net &= ~(LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP); - __attribute__((fallthrough)); - case 4: - rules.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV; - } - srv_debug(1, xs_fmt("lanlock abi: %d", abi)); - - ruleset_fd = syscall(SYS_landlock_create_ruleset, &rules, sizeof(struct landlock_ruleset_attr), 0); - if (ruleset_fd == -1) { - srv_debug(0, xs_fmt("landlock_create_ruleset failed: %s", strerror(errno))); - return; - } - -#define LL_R LANDLOCK_ACCESS_FS_READ_FILE -#define LL_X LANDLOCK_ACCESS_FS_EXECUTE -#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) -#define LL_RWCD (LL_RWCF | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_REMOVE_DIR) -#define LL_UNIX (LL_R | LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_MAKE_SOCK) -#define LL_CONN LANDLOCK_ACCESS_NET_CONNECT_TCP -#define LL_BIND LANDLOCK_ACCESS_NET_BIND_TCP - -#define LANDLOCK_PATH(p, r) do {\ - path.allowed_access = r;\ - if (abi < 2)\ - path.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER;\ - if (abi < 3)\ - path.allowed_access &= ~LANDLOCK_ACCESS_FS_TRUNCATE;\ - path.parent_fd = open(p, O_PATH | O_CLOEXEC);\ - if (path.parent_fd == -1) {\ - srv_debug(2, xs_fmt("open %s: %s", p, strerror(errno)));\ - goto close;\ - }\ - error = syscall(SYS_landlock_add_rule, ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path, 0); \ - if (error) {\ - srv_debug(0, xs_fmt("LANDLOCK_PATH(%s): %s", p, strerror(errno)));\ - goto close;\ - }\ -} while (0) - -#define LANDLOCK_PORT(p, r) do {\ - uint16_t _p = p;\ - net.port = _p;\ - net.allowed_access = r;\ - error = syscall(SYS_landlock_add_rule, ruleset_fd, LANDLOCK_RULE_NET_PORT, &net, 0);\ - if (error) {\ - srv_debug(0, xs_fmt("LANDLOCK_PORT(%d): %s", _p, strerror(errno)));\ - goto close;\ - }\ -} while (0) - - LANDLOCK_PATH(basedir, LL_RWCD); - LANDLOCK_PATH("/tmp", LL_RWCD); -#ifndef WITHOUT_SHM - LANDLOCK_PATH("/dev/shm", LL_RWCF); -#endif - LANDLOCK_PATH("/etc/resolv.conf", LL_R ); - LANDLOCK_PATH("/etc/hosts", LL_R ); - LANDLOCK_PATH("/etc/ssl/openssl.cnf", LL_R ); - LANDLOCK_PATH("/etc/ssl/cert.pem", LL_R ); - LANDLOCK_PATH("/usr/share/zoneinfo", LL_R ); - - if (*address == '/') - LANDLOCK_PATH(address, LL_UNIX); - - if (smail) - LANDLOCK_PATH("/usr/sbin/sendmail", LL_X); - - if (abi > 3) { - if (*address != '/') { - LANDLOCK_PORT( - (uint16_t)xs_number_get(xs_dict_get(srv_config, "port")), LL_BIND); - } - - LANDLOCK_PORT(80, LL_CONN); - LANDLOCK_PORT(443, LL_CONN); - } - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - srv_debug(0, xs_fmt("prctl SET_NO_NEW_PRIVS: %s", strerror(errno))); - goto close; - } - - if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0)) - srv_debug(0, xs_fmt("landlock_restrict_self: %s", strerror(errno))); + sbox_enter_linux_(basedir, address, smail); srv_log(xs_dup("landlocked")); -close: - close(ruleset_fd); - #endif }