Commit Diff


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 <op@omarpolo.com>
+ *
+ * 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 <linux/landlock.h>
+#include <linux/prctl.h>
+
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+
+#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;
 	}