aboutsummaryrefslogtreecommitdiff
path: root/src/xzdec
diff options
context:
space:
mode:
authorJia Tan <jiat0218@gmail.com>2023-12-19 21:18:28 +0800
committerJia Tan <jiat0218@gmail.com>2023-12-19 21:18:28 +0800
commitd74fb5f060b76db709b50f5fd37490394e52f975 (patch)
treeb212deb3b13080b6a21e8e7fdf2e570bc5b67827 /src/xzdec
parentliblzma: Initialize lzma_lz_encoder pointers with NULL. (diff)
downloadxz-d74fb5f060b76db709b50f5fd37490394e52f975.tar.xz
xzdec: Add sandbox support for Pledge, Capsicum, and Landlock.
A very strict sandbox is used when the last file is decompressed. The likely most common use case of xzdec is to decompress a single file. The Pledge sandbox is applied to the entire process with slightly more relaxed promises, until the last file is processed. Thanks to Christian Weisgerber for the initial patch adding Pledge sandboxing.
Diffstat (limited to 'src/xzdec')
-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);
}