commit 97799ccd4b67a81f97039305d4fdd66588da9962 from: Thomas Adam date: Sun Feb 06 20:15:21 2022 UTC portable: add support for landlock landlock is a new set of linux APIs that is conceptually similar to unveil(2): the idea is to restrict what a process can do on a specified part of the filesystem. There are some differences in the behaviour: the major one being that the landlock ruleset is inherited across execve(2). This just restricts the libexec helpers by completely revoking ANY filesystem access; after all they are the biggest attack surface. got send/fetch/clone *may* end up spawning ssh(1), so at the moment is not possible to landlock the main process. From Omar Polo. commit - 1f480907e29bc783454427371277ef33a25658e4 commit + 97799ccd4b67a81f97039305d4fdd66588da9962 blob - 958a3d67dfe92ddf1caf9fd8e3586f887a36001b blob + 0b90a45a21dee8de83282118e67277aef5bd3370 --- compat/Makefile.am +++ compat/Makefile.am @@ -31,6 +31,10 @@ else libopenbsd_compat_a_SOURCES += uuid.c endif +if HAVE_LINUX_LANDLOCK +libopenbsd_compat_a_SOURCES += landlock.c +endif + EXTRA_DIST = \ $(top_srcdir)/include/got_compat.h \ imsg.h \ blob - /dev/null blob + 47a5209dbfe20f149a142f16faaca86ca659120c (mode 644) --- /dev/null +++ compat/landlock.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 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 +#include + +#include +#include +#include +#include +#include +#include + +#include "got_compat.h" + +/* + * 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 + +/* + * Revoke any fs access. + */ +int +landlock_no_fs(void) +{ + struct landlock_ruleset_attr rattr = { + /* + * handled_access_fs can't be zero! Even if we don't + * add any path at all with landlock_add_rule, and thus + * rejecting *any* filesystem access, we still have to + * list some "possible actions" here. + */ + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, + }; + int fd, saved_errno; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) + return -1; + + fd = landlock_create_ruleset(&rattr, sizeof(rattr), 0); + if (fd == -1) { + /* this kernel doesn't have landlock built in */ + if (errno == ENOSYS || errno == ENOTSUP) + return 0; + return -1; + } + + if (landlock_restrict_self(fd, 0)) { + saved_errno = errno; + close(fd); + errno = saved_errno; + return -1; + } + + close(fd); + return 0; +} blob - a9b188be681d73c01d831cd71a266058697cd6ab blob + 44604f740f93329e013205265626d19b5450fe04 --- configure.ac +++ configure.ac @@ -42,6 +42,7 @@ AC_CHECK_HEADERS([ \ fcntl.h \ langinfo.h \ limits.h \ + linux/landlock.h \ locale.h \ netdb.h \ netinet/in.h \ @@ -163,6 +164,16 @@ AC_SUBST(PLATFORM) AM_CONDITIONAL([HOST_FREEBSD], [test "$PLATFORM" = "freebsd"]) AM_CONDITIONAL([HOST_LINUX], [test "$PLATFORM" = "linux"]) +# Landlock detection. +AC_MSG_CHECKING([for landlock]) +AM_CONDITIONAL([HAVE_LINUX_LANDLOCK], + [test "x$ac_cv_header_linux_landlock_h" = "xyes"]) +if test "x$ac_cv_header_linux_landlock_h" = "xyes"; then + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + # Clang sanitizers wrap reallocarray even if it isn't available on the target # system. When compiled it always returns NULL and crashes the program. To # detect this we need a more complicated test. blob - c07416a808a6799841c0c9978d004659680beda2 blob + 43e2e48d8e287757aa51bd8b89fa4d136904b88d --- include/got_compat.h +++ include/got_compat.h @@ -41,6 +41,12 @@ #ifndef __OpenBSD__ #define pledge(s, p) (0) #define unveil(s, p) (0) +#endif + +#ifndef HAVE_LINUX_LANDLOCK_H +#define landlock_no_fs() (0) +#else +int landlock_no_fs(void); #endif #ifndef INFTIM blob - 65c075409a0f6850f37b585385b6429b80a143ed blob + 5d5fc4944fb66148926a3e2aa218adf907063461 --- libexec/got-fetch-pack/got-fetch-pack.c +++ libexec/got-fetch-pack/got-fetch-pack.c @@ -809,6 +809,13 @@ main(int argc, char **argv) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif err = got_privsep_recv_imsg(&imsg, &ibuf, 0); if (err) { blob - b8572cfe5b35acf5d04456dc811ef5e1cf3b918d blob + bfaf074f9691171d53deb75a04179c2f13c5fbc3 --- libexec/got-index-pack/got-index-pack.c +++ libexec/got-index-pack/got-index-pack.c @@ -1008,6 +1008,13 @@ main(int argc, char **argv) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif err = got_privsep_recv_imsg(&imsg, &ibuf, 0); if (err) blob - 24a9911b4e2319200c86e0be0d525a3980763d80 blob + cea6f43adac14aa61fc01c67ea2223fe2ec4b367 --- libexec/got-read-blob/got-read-blob.c +++ libexec/got-read-blob/got-read-blob.c @@ -65,6 +65,13 @@ main(int argc, char *argv[]) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif for (;;) { blob - f324bf809bdb1dbc68030ee2676b88f4ec11dc4e blob + 75b69cf033167c2237a8a02aeebd0f48a8d649ea --- libexec/got-read-commit/got-read-commit.c +++ libexec/got-read-commit/got-read-commit.c @@ -119,6 +119,13 @@ main(int argc, char *argv[]) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif for (;;) { blob - 9820c8bd9e58d215598df363fdb6e25fbd81323d blob + 1ccc67e3c4467f8056bf23a367d62431de5219bd --- libexec/got-read-gitconfig/got-read-gitconfig.c +++ libexec/got-read-gitconfig/got-read-gitconfig.c @@ -341,6 +341,13 @@ main(int argc, char *argv[]) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif for (;;) { blob - 69f6999b8df42a9f230c521287de6e5aadebd128 blob + 3b954345ac46a733c1c785bd2af8facdac0a4f34 --- libexec/got-read-gotconfig/got-read-gotconfig.c +++ libexec/got-read-gotconfig/got-read-gotconfig.c @@ -498,6 +498,13 @@ main(int argc, char *argv[]) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif if (argc > 1) blob - 3d9bc64ad66ca532a2c58b8d50e3d4ab51abb4db blob + dc0a31bec0e6ec9ca5b23fbc47165af5c10063e5 --- libexec/got-read-object/got-read-object.c +++ libexec/got-read-object/got-read-object.c @@ -140,6 +140,13 @@ main(int argc, char *argv[]) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif for (;;) { blob - 760a54783d11db6cc8fed461bc956854e6b5f2ed blob + 722e0c736645f1324e288c3f065a34ec6f45c449 --- libexec/got-read-pack/got-read-pack.c +++ libexec/got-read-pack/got-read-pack.c @@ -1031,6 +1031,13 @@ main(int argc, char *argv[]) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif err = receive_packidx(&packidx, &ibuf); blob - 870e8ca6c8d4439248bc960e5d6797861de4de07 blob + 57787944b6605bdc56c8281731a46bb5a54f9442 --- libexec/got-read-tag/got-read-tag.c +++ libexec/got-read-tag/got-read-tag.c @@ -114,6 +114,13 @@ main(int argc, char *argv[]) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif for (;;) { blob - 35323190d763a2a21c96e0b0f6c3d19f6687f007 blob + 7a53a1be870ee3435986176c3800ecefc8647a04 --- libexec/got-read-tree/got-read-tree.c +++ libexec/got-read-tree/got-read-tree.c @@ -113,6 +113,13 @@ main(int argc, char *argv[]) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif for (;;) { blob - 5a7564d43fbe801786356a9a6036bb9372e66c68 blob + be27709918c6e0c9c853297e6344ade339339ed4 --- libexec/got-send-pack/got-send-pack.c +++ libexec/got-send-pack/got-send-pack.c @@ -599,6 +599,13 @@ main(int argc, char **argv) got_privsep_send_error(&ibuf, err); return 1; } + + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); + got_privsep_send_error(&ibuf, err); + return 1; + } #endif if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) { if (err->code == GOT_ERR_PRIVSEP_PIPE)