aboutsummaryrefslogtreecommitdiff
path: root/src/xz/file_io.c
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2024-02-17 23:07:35 +0200
committerLasse Collin <lasse.collin@tukaani.org>2024-02-17 23:07:35 +0200
commit374868d81d473ab56556a1cfd6b1b36a1fab348b (patch)
tree195da6ba1f931c4ec7783832428ea3c2bbcac2b5 /src/xz/file_io.c
parentxz: Tweak comments. (diff)
downloadxz-374868d81d473ab56556a1cfd6b1b36a1fab348b.tar.xz
xz: Move sandboxing code to sandbox.c and improve Landlock sandbox.
Landlock is now always used just like pledge(2) is: first in more permissive mode and later (under certain common conditions) in a strict mode that doesn't allow opening more files. I put pledge(2) first in sandbox.c because it's the simplest API to use and still somewhat fine-grained for basic applications. So it's the simplest thing to understand for anyone reading sandbox.c.
Diffstat (limited to '')
-rw-r--r--src/xz/file_io.c170
1 files changed, 2 insertions, 168 deletions
diff --git a/src/xz/file_io.c b/src/xz/file_io.c
index 876ee4de..678a9a5c 100644
--- a/src/xz/file_io.c
+++ b/src/xz/file_io.c
@@ -28,15 +28,6 @@ static bool warn_fchown;
# include <utime.h>
#endif
-#ifdef HAVE_CAP_RIGHTS_LIMIT
-# include <sys/capsicum.h>
-#endif
-
-#ifdef HAVE_LINUX_LANDLOCK_H
-# include <linux/landlock.h>
-# include <sys/syscall.h>
-#endif
-
#include "tuklib_open_stdxxx.h"
#ifdef _MSC_VER
@@ -92,11 +83,6 @@ typedef enum {
/// If true, try to create sparse files when decompressing.
static bool try_sparse = true;
-#ifdef ENABLE_SANDBOX
-/// True if the conditions for sandboxing (described in main()) have been met.
-static bool sandbox_allowed = false;
-#endif
-
#ifndef TUKLIB_DOSLIKE
/// File status flags of standard input. This is used by io_open_src()
/// and io_close_src().
@@ -181,159 +167,6 @@ io_no_sparse(void)
}
-#ifdef ENABLE_SANDBOX
-extern void
-io_allow_sandbox(void)
-{
- sandbox_allowed = true;
- return;
-}
-
-
-/// Enables operating-system-specific sandbox if it is possible.
-/// src_fd is the file descriptor of the input file.
-static void
-io_sandbox_enter(int src_fd)
-{
- if (!sandbox_allowed) {
- // This message is more often annoying than useful so
- // it's commented out. It can be useful when developing
- // the sandboxing code.
- //message(V_DEBUG, _("Sandbox is disabled due "
- // "to incompatible command line arguments"));
- return;
- }
-
- const char dummy_str[] = "x";
-
- // Try to ensure that both libc and xz locale files have been
- // loaded when NLS is enabled.
- snprintf(NULL, 0, "%s%s", _(dummy_str), strerror(EINVAL));
-
- // Try to ensure that iconv data files needed for handling multibyte
- // characters have been loaded. This is needed at least with glibc.
- tuklib_mbstr_width(dummy_str, NULL);
-
-#ifdef 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_EVENT, CAP_FCNTL, CAP_LOOKUP, CAP_READ, CAP_SEEK)))
- 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_EVENT, CAP_FCNTL, CAP_FSTAT, CAP_LOOKUP,
- CAP_WRITE, CAP_SEEK)))
- goto error;
-
- if (cap_rights_limit(STDERR_FILENO, cap_rights_init(&rights,
- CAP_WRITE)))
- goto error;
-
- if (cap_rights_limit(user_abort_pipe[0], cap_rights_init(&rights,
- CAP_EVENT)))
- goto error;
-
- if (cap_rights_limit(user_abort_pipe[1], cap_rights_init(&rights,
- CAP_WRITE)))
- goto error;
-
-#elif defined(HAVE_PLEDGE)
- // pledge() was introduced in OpenBSD 5.9.
- //
- // main() unconditionally calls pledge() with fairly relaxed
- // promises which work in all situations. Here we make the
- // sandbox more strict.
- 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;
-
- // We want to set all supported flags in handled_access_fs.
- // This way the ruleset will initially forbid access to all
- // actions that the available Landlock ABI version supports.
- // Exceptions can be added using landlock_add_rule(2) to
- // allow certain actions on certain files or directories.
- //
- // The same flag values are used on all archs. ABI v2 and v3
- // both add one new flag.
- //
- // First in ABI v1: LANDLOCK_ACCESS_FS_EXECUTE = 1ULL << 0
- // Last in ABI v1: LANDLOCK_ACCESS_FS_MAKE_SYM = 1ULL << 12
- // Last in ABI v2: LANDLOCK_ACCESS_FS_REFER = 1ULL << 13
- // Last in ABI v3: LANDLOCK_ACCESS_FS_TRUNCATE = 1ULL << 14
- //
- // This makes it simple to set the mask based on the ABI
- // version and we don't need to care which flags are #defined
- // in the installed <linux/landlock.h>.
- 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.
- //
- // NOTE: It's possible that the hack at the beginning of this
- // function isn't be good enough. It tries to get translations
- // and libc-specific files loaded but if it's not good enough
- // then perhaps a Landlock rule to allow reading from /usr
- // and/or the xz installation prefix would be needed.
- //
- // prctl(PR_SET_NO_NEW_PRIVS, ...) was already called in
- // main() so we don't do it here again.
- 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
-
- // This message is annoying in xz -lvv.
- //message(V_DEBUG, _("Sandbox was successfully enabled"));
- 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 xz will silently run without
- // the sandbox.
- if (errno == ENOSYS)
- return;
-#endif
- message_fatal(_("Failed to enable the sandbox"));
-}
-#endif // ENABLE_SANDBOX
-
-
#ifndef TUKLIB_DOSLIKE
/// \brief Waits for input or output to become available or for a signal
///
@@ -889,7 +722,8 @@ io_open_src(const char *src_name)
#ifdef ENABLE_SANDBOX
if (!error)
- io_sandbox_enter(pair.src_fd);
+ sandbox_enable_strict_if_allowed(pair.src_fd,
+ user_abort_pipe[0], user_abort_pipe[1]);
#endif
return error ? NULL : &pair;