aboutsummaryrefslogtreecommitdiff
path: root/sandbox.c
diff options
context:
space:
mode:
authorOmar Polo <op@omarpolo.com>2023-08-23 20:18:59 +0000
committerOmar Polo <op@omarpolo.com>2023-08-23 20:18:59 +0000
commit4d9d3093d48025a1a66c125f7878a094cf2c9d10 (patch)
treeb41280c9e32b33a7dc380d6abfbca08fded10554 /sandbox.c
parentcedef5b09c13d8fac119a7ee5595ee253c2a37b4 (diff)
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.)
Diffstat (limited to 'sandbox.c')
-rw-r--r--sandbox.c207
1 files changed, 207 insertions, 0 deletions
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 <op@omarpolo.com>
+ * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org>
*
* 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 <linux/landlock.h>
+
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+
+#include <errno.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
+
+/*
+ * 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