commit 2c6298d5fc88053078cbd53b62af8647209fda58 from: Thomas Adam date: Fri Sep 24 23:06:06 2021 UTC Landlock support: WIP This is a WIP commit to support Landlock; sandboxing similar to unveil() commit - cdf9da3e2fae83e739d1b49c5843b05b813001b7 commit + 2c6298d5fc88053078cbd53b62af8647209fda58 blob - 77a4b663e28d1206c2b7ed331a3fef25b9a5e44d blob + 48cc1d59b9f7623179112138286da1cb4e2e5868 --- compat/Makefile.am +++ compat/Makefile.am @@ -37,6 +37,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 + 5d140e52903f80c37f07926458aed487d6e2b102 (mode 644) --- /dev/null +++ compat/landlock.c @@ -0,0 +1,215 @@ +/* + * 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. + */ + +/* + * This an implementation of an OpenBSD' unveil(2) compatible API on + * top of Linux' landlock. + */ + +#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 + +static int landlock_fd = -1; + +static int +open_landlock(void) +{ + struct landlock_ruleset_attr rattr = { + .handled_access_fs = 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, + }; + + return landlock_create_ruleset(&rattr, sizeof(rattr), 0); +} + +static int +landlock_apply(void) +{ + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) + return -1; + + if (landlock_restrict_self(landlock_fd, 0)) + return -1; + + close(landlock_fd); + landlock_fd = -1; + return 0; +} + +static int +parse_permissions(const char *permission) +{ + int perm = 0; + + for (; *permission; ++permission) { + switch (*permission) { + case 'r': + perm |= LANDLOCK_ACCESS_FS_READ_FILE; + perm |= LANDLOCK_ACCESS_FS_READ_DIR; + break; + case 'w': + perm |= LANDLOCK_ACCESS_FS_WRITE_FILE; + break; + case 'x': + perm |= LANDLOCK_ACCESS_FS_EXECUTE; + break; + case 'c': + perm |= LANDLOCK_ACCESS_FS_REMOVE_DIR; + perm |= LANDLOCK_ACCESS_FS_REMOVE_FILE; + perm |= LANDLOCK_ACCESS_FS_MAKE_CHAR; + perm |= LANDLOCK_ACCESS_FS_MAKE_DIR; + perm |= LANDLOCK_ACCESS_FS_MAKE_REG; + perm |= LANDLOCK_ACCESS_FS_MAKE_SOCK; + perm |= LANDLOCK_ACCESS_FS_MAKE_FIFO; + perm |= LANDLOCK_ACCESS_FS_MAKE_BLOCK; + perm |= LANDLOCK_ACCESS_FS_MAKE_SYM; + break; + default: + return -1; + } + } + + return perm; +} + +static int +landlock_unveil_path(const char *path, int permissions) +{ + struct landlock_path_beneath_attr pb; + struct stat sb; + int fd, err, saved_errno; + char fpath[PATH_MAX]; + + pb.allowed_access = permissions; + if ((pb.parent_fd = open(path, O_PATH)) == -1) + return -1; + + if (fstat(pb.parent_fd, &sb) == -1) + return -1; + + if (!S_ISDIR(sb.st_mode)) { + close(pb.parent_fd); + + if (strlcpy(fpath, path, sizeof(fpath)) >= sizeof(fpath)) { + errno = ENAMETOOLONG; + return -1; + } + + permissions |= LANDLOCK_ACCESS_FS_READ_FILE; + permissions |= LANDLOCK_ACCESS_FS_READ_DIR; + return landlock_unveil_path(dirname(fpath), permissions); + } + + err = landlock_add_rule(landlock_fd, LANDLOCK_RULE_PATH_BENEATH, + &pb, 0); + saved_errno = errno; + close(pb.parent_fd); + errno = saved_errno; + return err ? -1 : 0; +} + +int +landlock_unveil(const char *path, const char *permissions) +{ + int perms; + + if (landlock_fd == -1) { + if ((landlock_fd = open_landlock()) == -1) + return -1; + + /* XXX: use rpath on the current executable */ + if (landlock_unveil("/lib64", "rx") == -1) + return -1; + } + + if (path == NULL && permissions == NULL) + return landlock_apply(); + + if (path == NULL || + permissions == NULL || + (perms = parse_permissions(permissions)) == -1) { + errno = EINVAL; + return -1; + } + + return landlock_unveil_path(path, perms); +} + +int +landlock_no_fs(void) +{ + if ((landlock_fd = open_landlock()) == -1) + return -1; + + return landlock_apply(); +} blob - c3f6d3a3df96207adb3645a88553071f00fe841e blob + 8332b776bffa8dbe2ccf39f66d2ad1c81d747a6f --- 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 - 78da94b5a6a66a86e58077352388589e6c6a18f2 blob + bf5dfd2ee615529fa9ff4ec828d00e3444258e19 --- include/got_compat.h +++ include/got_compat.h @@ -43,6 +43,13 @@ #define unveil(s, p) (0) #endif +#ifdef HAVE_LINUX_LANDLOCK_H +int landlock_no_fs(void); +int landlock_unveil(const char *, const char *); +/* #undef unveil */ +/* #define unveil(s, p) landlock_unveil((s), (p)) */ +#endif + #ifndef INFTIM #define INFTIM -1 #endif blob - 0b62c26c8990e23ab00c91090588f0e298ef258e blob + 33d4492acece03853644ae13bbab06837b26673c --- libexec/got-fetch-pack/got-fetch-pack.c +++ libexec/got-fetch-pack/got-fetch-pack.c @@ -806,6 +806,14 @@ main(int argc, char **argv) /* revoke access to most system calls */ if (pledge("stdio recvfd", NULL) == -1) { err = got_error_from_errno("pledge"); + got_privsep_send_error(&ibuf, err); + return 1; + } +#endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); got_privsep_send_error(&ibuf, err); return 1; } blob - 01ca110a1eb4d1461ce37f77a21391e3d8322e9a blob + 640b9524be6ddbe8b0f040b7a8f13049072a3e7d --- libexec/got-index-pack/got-index-pack.c +++ libexec/got-index-pack/got-index-pack.c @@ -1002,6 +1002,14 @@ main(int argc, char **argv) /* revoke access to most system calls */ if (pledge("stdio recvfd", NULL) == -1) { err = got_error_from_errno("pledge"); + got_privsep_send_error(&ibuf, err); + return 1; + } +#endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); got_privsep_send_error(&ibuf, err); return 1; } blob - 24a9911b4e2319200c86e0be0d525a3980763d80 blob + 0fc3baed8db3b8064d20a7b14eea4028f153181c --- libexec/got-read-blob/got-read-blob.c +++ libexec/got-read-blob/got-read-blob.c @@ -66,6 +66,14 @@ main(int argc, char *argv[]) return 1; } #endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* 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 (;;) { struct imsg imsg, imsg_outfd; blob - f324bf809bdb1dbc68030ee2676b88f4ec11dc4e blob + 2514f144c7d8039bede77eada03abb583ae8d358 --- libexec/got-read-commit/got-read-commit.c +++ libexec/got-read-commit/got-read-commit.c @@ -120,6 +120,14 @@ main(int argc, char *argv[]) return 1; } #endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* 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 (;;) { struct imsg imsg; blob - 465fc301b0dceaeef13bda6ff6eb82c76ed7eac6 blob + a5468d258ab5f8e41c24bd4c46dd0f328f83e298 --- libexec/got-read-gitconfig/got-read-gitconfig.c +++ libexec/got-read-gitconfig/got-read-gitconfig.c @@ -338,6 +338,14 @@ main(int argc, char *argv[]) /* revoke access to most system calls */ if (pledge("stdio recvfd", NULL) == -1) { err = got_error_from_errno("pledge"); + got_privsep_send_error(&ibuf, err); + return 1; + } +#endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); got_privsep_send_error(&ibuf, err); return 1; } blob - 69f6999b8df42a9f230c521287de6e5aadebd128 blob + 69c0ee8c56a5bbd4f2f3d7210fe488a1e8c9c4fb --- libexec/got-read-gotconfig/got-read-gotconfig.c +++ libexec/got-read-gotconfig/got-read-gotconfig.c @@ -495,6 +495,14 @@ main(int argc, char *argv[]) /* revoke access to most system calls */ if (pledge("stdio recvfd", NULL) == -1) { err = got_error_from_errno("pledge"); + got_privsep_send_error(&ibuf, err); + return 1; + } +#endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); got_privsep_send_error(&ibuf, err); return 1; } blob - 3d9bc64ad66ca532a2c58b8d50e3d4ab51abb4db blob + 61685952983f473bec95c8089ad61e741883aade --- libexec/got-read-object/got-read-object.c +++ libexec/got-read-object/got-read-object.c @@ -141,6 +141,14 @@ main(int argc, char *argv[]) return 1; } #endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* 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 (;;) { if (sigint_received) { blob - a5318c83459bae63af9da826aa7e3c4ebb0e683c blob + 52bba3b22b8d0992a96c10ebcd8caad4cb8b0b61 --- libexec/got-read-pack/got-read-pack.c +++ libexec/got-read-pack/got-read-pack.c @@ -1016,6 +1016,14 @@ main(int argc, char *argv[]) /* revoke access to most system calls */ if (pledge("stdio recvfd", NULL) == -1) { err = got_error_from_errno("pledge"); + got_privsep_send_error(&ibuf, err); + return 1; + } +#endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); got_privsep_send_error(&ibuf, err); return 1; } blob - 870e8ca6c8d4439248bc960e5d6797861de4de07 blob + 173a89d3b9a9790f29f56458b7148e817de8d759 --- libexec/got-read-tag/got-read-tag.c +++ libexec/got-read-tag/got-read-tag.c @@ -115,6 +115,14 @@ main(int argc, char *argv[]) return 1; } #endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* 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 (;;) { struct imsg imsg; blob - 35323190d763a2a21c96e0b0f6c3d19f6687f007 blob + c3c2bf44d1f72777a3407f505c92247df15cc264 --- libexec/got-read-tree/got-read-tree.c +++ libexec/got-read-tree/got-read-tree.c @@ -114,6 +114,14 @@ main(int argc, char *argv[]) return 1; } #endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* 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 (;;) { struct imsg imsg; blob - 91674461564887f691b79dc590d4f9199284f2df blob + 649684871c1d73ced3b08fe12451e38a978b484c --- libexec/got-send-pack/got-send-pack.c +++ libexec/got-send-pack/got-send-pack.c @@ -595,6 +595,14 @@ main(int argc, char **argv) /* revoke access to most system calls */ if (pledge("stdio recvfd", NULL) == -1) { err = got_error_from_errno("pledge"); + got_privsep_send_error(&ibuf, err); + return 1; + } +#endif +#ifdef HAVE_LINUX_LANDLOCK_H + /* revoke fs access */ + if (landlock_no_fs() == -1) { + err = got_error_from_errno("landlock_no_fs"); got_privsep_send_error(&ibuf, err); return 1; }