diff --git a/ChangeLog b/ChangeLog index eeee9e1..e39050a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +vNEXT: + Support for the Landlock LSM. After initial setup, + sslh gives up all local file access rights. + v2.0.1: Fix resolve_on_forward setting, which would crash sslh reliably. diff --git a/Makefile b/Makefile index b7bc708..8e7bde5 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files) USELIBEV=1 # Use libev? USELIBWRAP?= # Use libwrap? USELIBCAP= # Use libcap? +USELANDLOCK=1 # Use the landlock LSM? USESYSTEMD= # Make use of systemd socket activation USELIBBSD?= # Use libbsd (needed to update process name in `ps`) COV_TEST= # Perform test coverage? @@ -35,7 +36,7 @@ CFLAGS +=-Wall -O2 -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN) LIBS=-lm -lpcre2-8 -OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o tcp-probe.o +OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o tcp-probe.o landlock.o OBJS_A=libsslh.a FORK_OBJS=sslh-fork.o $(OBJS_A) SELECT_OBJS=processes.o udp-listener.o sslh-select.o hash.o tcp-listener.o $(OBJS_A) @@ -77,6 +78,12 @@ ifneq ($(strip $(USELIBEV)),) CONDITIONAL_TARGETS+=sslh-ev endif +ifneq ($(strip $(USELANDLOCK)),) + CPPFLAGS+=-DLANDLOCK +endif + + + all: sslh-fork sslh-select $(MAN) echosrv $(CONDITIONAL_TARGETS) %.o: %.c %.h version.h diff --git a/common.h b/common.h index 6a011f4..c8399e1 100644 --- a/common.h +++ b/common.h @@ -183,4 +183,8 @@ void start_shoveler(int); void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen); +/* landlock.c */ +void setup_landlock(void); + + #endif diff --git a/landlock.c b/landlock.c new file mode 100644 index 0000000..1e53b9c --- /dev/null +++ b/landlock.c @@ -0,0 +1,111 @@ +/* + * Setup a sandbox using the Landlock LSM, if available. + +# Copyright (C) 2023 Yves Rutschle +# +# This program is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more +# details. +# +# The full text for the General Public License is here: +# http://www.gnu.org/licenses/gpl.html +# +*/ + +#ifdef LANDLOCK + +#define _GNU_SOURCE +#include +#include +#include + +#include "log.h" + +#ifndef landlock_create_ruleset +static inline int +landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, + const size_t size, const __u32 flags) +{ + return syscall(__NR_landlock_create_ruleset, attr, size, flags); +} +#endif + +#ifndef landlock_add_rule +static inline int landlock_add_rule(const int ruleset_fd, + const enum landlock_rule_type rule_type, + const void *const rule_attr, + const __u32 flags) +{ + return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, + flags); +} +#endif + +#ifndef landlock_restrict_self +static inline int landlock_restrict_self(const int ruleset_fd, + const __u32 flags) +{ + return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); +} +#endif + + +void setup_landlock(void) +{ + __u64 restrict_rules = + LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR | + LANDLOCK_ACCESS_FS_WRITE_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; + + struct landlock_ruleset_attr ruleset_attr = { + .handled_access_fs = restrict_rules + }; + + /* ruleset_addr.handled_access_fs contains all rights that will be restricted + * unless explicitly added */ + int ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + if (ruleset_fd < 0) { + print_message(msg_config_error, "Landlock: Failed to create a ruleset"); + return; + } + + /* No call to landlock_add_rule: we retain no rights whatsoever */ + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + print_message(msg_config_error, "Landlock: Failed to restrict privileges"); + return; + } + if (landlock_restrict_self(ruleset_fd, 0)) { + print_message(msg_config_error, "Landlock: Failed to enforce ruleset"); + return; + } + close(ruleset_fd); + + print_message(msg_config, "Landlock: all restricted\n"); +} + +#else +void setup_landlock(void) +{ + return; +} +#endif diff --git a/sslh-main.c b/sslh-main.c index fcabfaa..9b02835 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -283,6 +283,7 @@ int main(int argc, char *argv[], char* envp[]) if (cfg.user || cfg.chroot) drop_privileges(cfg.user, cfg.chroot); + setup_landlock(); printcaps();