aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/xzdec/xzdec.c146
1 files changed, 139 insertions, 7 deletions
diff --git a/src/xzdec/xzdec.c b/src/xzdec/xzdec.c
index 10ce4e82..6eda6a1f 100644
--- a/src/xzdec/xzdec.c
+++ b/src/xzdec/xzdec.c
@@ -21,6 +21,21 @@
# include <unistd.h>
#endif
+#ifdef HAVE_CAP_RIGHTS_LIMIT
+# include <sys/capsicum.h>
+#endif
+
+#ifdef HAVE_LINUX_LANDLOCK_H
+# include <linux/landlock.h>
+# include <sys/prctl.h>
+# include <sys/syscall.h>
+#endif
+
+#if defined(HAVE_CAP_RIGHTS_LIMIT) || defined(HAVE_PLEDGE) \
+ || defined(HAVE_LINUX_LANDLOCK_H)
+# define ENABLE_SANDBOX 1
+#endif
+
#include "getopt.h"
#include "tuklib_progname.h"
#include "tuklib_exit.h"
@@ -280,9 +295,107 @@ uncompress(lzma_stream *strm, FILE *file, const char *filename)
}
+#ifdef ENABLE_SANDBOX
+static void
+sandbox_enter(int src_fd)
+{
+#if defined(HAVE_CAP_RIGHTS_LIMIT)
+ // Capsicum needs FreeBSD 10.2 or later.
+ cap_rights_t rights;
+
+ if (cap_enter())
+ goto error;
+
+ if (cap_rights_limit(src_fd, cap_rights_init(&rights, CAP_READ)))
+ goto error;
+
+ // If not reading from stdin, remove all capabilities from it.
+ if (src_fd != STDIN_FILENO && cap_rights_limit(
+ STDIN_FILENO, cap_rights_clear(&rights)))
+ goto error;
+
+ if (cap_rights_limit(STDOUT_FILENO, cap_rights_init(&rights, CAP_WRITE)))
+ goto error;
+
+ if (cap_rights_limit(STDERR_FILENO, cap_rights_init(&rights, CAP_WRITE)))
+ goto error;
+
+#elif defined(HAVE_PLEDGE)
+ // pledge() was introduced in OpenBSD 5.9.
+ if (pledge("stdio", ""))
+ goto error;
+
+ (void)src_fd;
+#elif defined(HAVE_LINUX_LANDLOCK_H)
+ int landlock_abi = syscall(SYS_landlock_create_ruleset,
+ (void *)NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
+
+ if (landlock_abi > 0) {
+ // We support ABI versions 1-3.
+ if (landlock_abi > 3)
+ landlock_abi = 3;
+
+ const struct landlock_ruleset_attr attr = {
+ .handled_access_fs = (1ULL << (12 + landlock_abi)) - 1
+ };
+
+ const int ruleset_fd = syscall(SYS_landlock_create_ruleset,
+ &attr, sizeof(attr), 0U);
+ if (ruleset_fd < 0)
+ goto error;
+
+ // All files we need should have already been opened. Thus,
+ // we don't need to add any rules using landlock_add_rule(2)
+ // before activating the sandbox.
+ if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0U) != 0)
+ goto error;
+ }
+
+ (void)src_fd;
+#else
+# error ENABLE_SANDBOX is defined but no sandboxing method was found.
+#endif
+
+ return;
+
+error:
+#ifdef HAVE_CAP_RIGHTS_LIMIT
+ // If a kernel is configured without capability mode support or
+ // used in an emulator that does not implement the capability
+ // system calls, then the Capsicum system calls will fail and set
+ // errno to ENOSYS. In that case xzdec will silently run without
+ // the sandbox.
+ if (errno == ENOSYS)
+ return;
+#endif
+ my_errorf("Failed to enable the sandbox");
+ exit(EXIT_FAILURE);
+}
+#endif
+
+
int
main(int argc, char **argv)
{
+#ifdef HAVE_PLEDGE
+ // OpenBSD's pledge(2) sandbox.
+ // Initially enable the sandbox slightly more relaxed so that
+ // the process can still open files. This allows the sandbox to
+ // be enabled when parsing command line arguments and decompressing
+ // all files (the more strict sandbox only restricts the last file
+ // that is decompressed).
+ if (pledge("stdio rpath", "")) {
+ my_errorf("Failed to enable the sandbox");
+ exit(EXIT_FAILURE);
+ }
+#endif
+
+#ifdef HAVE_LINUX_LANDLOCK_H
+ // Prevent the process from gaining new privileges. The return
+ // is ignored to keep compatibility with old kernels.
+ (void)prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+#endif
+
// Initialize progname which we will be used in error messages.
tuklib_progname_init(argv);
@@ -302,24 +415,43 @@ main(int argc, char **argv)
if (optind == argc) {
// No filenames given, decode from stdin.
+#ifdef ENABLE_SANDBOX
+ sandbox_enter(STDIN_FILENO);
+#endif
uncompress(&strm, stdin, "(stdin)");
} else {
// Loop through the filenames given on the command line.
do {
+ FILE *src_file;
+ const char *src_name;
+
// "-" indicates stdin.
if (strcmp(argv[optind], "-") == 0) {
- uncompress(&strm, stdin, "(stdin)");
+ src_file = stdin;
+ src_name = "(stdin)";
} else {
- FILE *file = fopen(argv[optind], "rb");
- if (file == NULL) {
- my_errorf("%s: %s", argv[optind],
+ src_name = argv[optind];
+ src_file = fopen(src_name, "rb");
+ if (src_file == NULL) {
+ my_errorf("%s: %s", src_name,
strerror(errno));
exit(EXIT_FAILURE);
}
-
- uncompress(&strm, file, argv[optind]);
- fclose(file);
}
+#ifdef ENABLE_SANDBOX
+ // Enable the sandbox for the last file. When the
+ // strict sandbox is enabled the process can no
+ // longer open additional files. It is likely that
+ // the most common way to use xzdec is to
+ // decompress a single file, so this fully protects
+ // most use cases.
+ if (optind == argc - 1)
+ sandbox_enter(fileno(src_file));
+#endif
+ uncompress(&strm, src_file, src_name);
+
+ if (src_file != stdin)
+ fclose(src_file);
} while (++optind < argc);
}