From 8930ec395e771f9e4ffef0530915feee776558ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20R=C5=B1tschl=C3=A9?= Date: Tue, 29 Aug 2023 17:05:41 +0200 Subject: [PATCH] Initial support for the landlock LSM --- ChangeLog | 4 ++ Makefile | 7 +++- common.h | 4 ++ landlock.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sslh-main.c | 1 + 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 landlock.c diff --git a/ChangeLog b/ChangeLog index 1d24e02..3c200f4 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: New sslh-ev: this is functionally equivalent to sslh-select (mono-process, only forks for specified diff --git a/Makefile b/Makefile index ec6b46b..49ee89e 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ ENABLE_REGEX=1 # Enable regex probes USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files) 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? @@ -33,7 +34,7 @@ AR ?= ar 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) @@ -71,6 +72,10 @@ ifneq ($(strip $(USELIBBSD)),) CPPFLAGS+=-DLIBBSD endif +ifneq ($(strip $(USELANDLOCK)),) + CPPFLAGS+=-DLANDLOCK +endif + all: sslh $(MAN) echosrv $(CONDITIONAL_TARGETS) diff --git a/common.h b/common.h index 6f6e76c..4b457ab 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 b5ead6d..1ec6221 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -277,6 +277,7 @@ int main(int argc, char *argv[], char* envp[]) if (cfg.user || cfg.chroot) drop_privileges(cfg.user, cfg.chroot); + setup_landlock(); printcaps();