From 4d9d3093d48025a1a66c125f7878a094cf2c9d10 Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Wed, 23 Aug 2023 20:18:59 +0000 Subject: [PATCH] resurrect landlock support this time targetting ABI level 3; partially based on how claudio@ handled it in rpki-client. Fun how this bit of code has come full circle (gmid inspired what I wrote for got, which inspired what was written for rpki-client, which has come back.) --- Makefile | 8 +- configure | 2 + have/Makefile | 1 + have/landlock.c | 42 ++++++++++ sandbox.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 have/landlock.c diff --git a/Makefile b/Makefile index dd6e6d4..44dae1b 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ GG_OBJS = ${GG_SRCS:.c=.o} ${COBJS} TITAN_SRCS = titan.c iri.c log.c utf8.c TITAN_OBJS = ${TITAN_SRCS:.c=.o} ${COBJS} -SRCS = gmid.h log.h parse.y proc.h \ +SRCS = gmid.h landlock_shim.h log.h parse.y proc.h \ ${GMID_SRCS} ${GEMEXP_SRCS} ${GG_SRCS} ${TITAN_SRCS} DISTNAME = gmid-${VERSION} @@ -130,9 +130,9 @@ lint: DISTFILES = .cirrus.yml .dockerignore .gitignore ChangeLog LICENSE \ Makefile README.md config.c configure configure.local.example \ crypto.c dirs.c fcgi.c ge.c gemexp.1 gg.1 gg.c gmid.8 gmid.c \ - gmid.conf.5 gmid.h iri.c iri.h log.c log.h logger.c mime.c \ - parse.y proc.c proc.h proxy.c puny.c sandbox.c server.c \ - titan.1 titan.c utf8.c utils.c y.tab.c + gmid.conf.5 gmid.h iri.c iri.h landlock_shim.h log.c log.h \ + logger.c mime.c parse.y proc.c proc.h proxy.c puny.c \ + sandbox.c server.c titan.1 titan.c utf8.c utils.c y.tab.c release: sed -i -e '/^RELEASE=/s/no/yes' configure diff --git a/configure b/configure index c77f13c..cedf595 100755 --- a/configure +++ b/configure @@ -289,6 +289,7 @@ runtest getdtablecount GETDTABLECOUNT || true runtest getdtablesize GETDTABLESIZE || true runtest getprogname GETPROGNAME || true runtest imsg IMSG "" -lutil libimsg || true +runtest landlock LANDLOCK || true runtest libevent LIBEVENT "" -levent libevent_core|| true runtest memmem MEMMEM -D_GNU_SOURCE || true runtest openssl OPENSSL "" '-lcrypto -lssl' 'libcrypto libssl' || true @@ -431,6 +432,7 @@ cat <<__HEREDOC__ #define VERSION "${VERSION}" #define DISABLE_SANDBOX ${DISABLE_SANDBOX} +#define HAVE_LANDLOCK ${HAVE_LANDLOCK} __HEREDOC__ diff --git a/have/Makefile b/have/Makefile index d2a1dfa..b9ff6ec 100644 --- a/have/Makefile +++ b/have/Makefile @@ -15,6 +15,7 @@ DISTFILES = ASN1_time_parse.c \ getentropy.c \ getprogname.c \ imsg.c \ + landlock.c \ libevent.c \ libevent2.c \ libtls.c \ diff --git a/have/landlock.c b/have/landlock.c new file mode 100644 index 0000000..6548e88 --- /dev/null +++ b/have/landlock.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * 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. + */ + +#include +#include +#include +#include + +#ifndef landlock_create_ruleset +static inline int +landlock_create_ruleset(const struct landlock_ruleset_attr *attr, size_t size, + __u32 flags) +{ + return syscall(__NR_landlock_create_ruleset, attr, size, flags); +} +#endif + +int +main(void) +{ + int rfd; + const struct landlock_ruleset_attr rsattr = { + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR + }; + + rfd = landlock_create_ruleset(&rsattr, sizeof(rsattr), 0); + return rfd == -1; +} diff --git a/sandbox.c b/sandbox.c index 0e7e4ad..0e17110 100644 --- a/sandbox.c +++ b/sandbox.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, 2023 Omar Polo + * Copyright (c) 2022 Claudio Jeker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -49,6 +50,212 @@ sandbox_logger_process(void) fatal("pledge"); } +#elif HAVE_LANDLOCK + +#include + +#include +#include +#include + +#include + +/* + * What's the deal with landlock? While distro with linux >= 5.13 + * have the struct declarations, libc wrappers are missing. The + * sample landlock code provided by the authors includes these "shims" + * in their example for the landlock API until libc provides them. + */ + +#ifndef landlock_create_ruleset +static inline int +landlock_create_ruleset(const struct landlock_ruleset_attr *attr, size_t size, + __u32 flags) +{ + return syscall(__NR_landlock_create_ruleset, attr, size, flags); +} +#endif + +#ifndef landlock_add_rule +static inline int +landlock_add_rule(int ruleset_fd, enum landlock_rule_type type, + const void *attr, __u32 flags) +{ + return syscall(__NR_landlock_add_rule, ruleset_fd, type, attr, flags); +} +#endif + +#ifndef landlock_restrict_self +static inline int +landlock_restrict_self(int ruleset_fd, __u32 flags) +{ + return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); +} +#endif + +/* + * Maybe we should ship with a full copy of the linux headers because + * you never know... + */ + +#ifndef LANDLOCK_ACCESS_FS_REFER +#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) +#endif + +#ifndef LANDLOCK_ACCESS_FS_TRUNCATE +#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14) +#endif + +static int landlock_state; +static int landlock_fd; + +/* + * Initialize landlock, which is stupidly complicated. + */ +static int +landlock_init(void) +{ + struct landlock_ruleset_attr rattr = { + /* + * List all capabilities currently defined by landlock. + * Failure in doing so will implicitly allow those actions + * (i.e. omitting READ_FILE will allow to read _any_ file.) + */ + .handled_access_fs = + 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 | + LANDLOCK_ACCESS_FS_TRUNCATE, + }; + int fd, abi, saved_errno; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) + return -1; + + abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + if (abi == -1) + return -1; + if (abi < 2) + rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; + if (abi < 3) + rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE; + + landlock_state = 1; + return landlock_create_ruleset(&rattr, sizeof(rattr), 0); +} + +static int +landlock_lock(void) +{ + int saved_errno; + + if (landlock_restrict_self(landlock_fd, 0)) { + saved_errno = errno; + close(landlock_fd); + errno = saved_errno; + landlock_state = -1; + return -1; + } + + landlock_state = 2; + close(landlock_fd); + return 0; +} + +static int +landlock_unveil(const char *path, const char *permissions) +{ + struct landlock_path_beneath_attr lpba; + int fd, saved_errno; + + if (landlock_state == 0) { + if ((landlock_fd = landlock_init()) == -1) { + landlock_state = -1; + /* this kernel doesn't have landlock built in */ + if (errno == ENOSYS || errno == EOPNOTSUPP) + return 0; + return -1; + } + } + + /* no landlock available */ + if (landlock_state == -1) + return 0; + + if (path == NULL && permissions == NULL) + return landlock_lock(); + + if (path == NULL || permissions == NULL || landlock_state != 1) { + errno = EINVAL; + return -1; + } + + if (!strcmp(permissions, "r")) { + fd = open(path, O_PATH | O_CLOEXEC); + if (fd == -1) + return -1; + lpba = (struct landlock_path_beneath_attr){ + .allowed_access = + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR, + .parent_fd = fd, + }; + if (landlock_add_rule(landlock_fd, LANDLOCK_RULE_PATH_BENEATH, + &lpba, 0) == -1) { + saved_errno = errno; + close(fd); + errno = saved_errno; + return -1; + } + close(fd); + } + + return 0; +} + +void +sandbox_main_process(void) +{ + return; +} + +void +sandbox_server_process(void) +{ + if (landlock_unveil("/", "r") == -1) + fatal("landlock_unveil(/)"); + + if (landlock_unveil(NULL, NULL) == -1) + fatal("landlock_unveil(NULL, NULL)"); +} + +void +sandbox_crypto_process(void) +{ + /* contrary to unveil(2), this removes all fs access. */ + if (landlock_unveil(NULL, NULL) == -1) + fatal("landlock_unveil(NULL, NULL)"); +} + +void +sandbox_logger_process(void) +{ + /* contrary to unveil(2), this removes all fs access. */ + if (landlock_unveil(NULL, NULL) == -1) + fatal("landlock_unveil(NULL, NULL)"); +} + #else void