diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-15 09:41:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-15 09:41:34 +0000 |
commit | 25e43e389c26d56f90f2f20e3cee19c808b2a18c (patch) | |
tree | fdadca249ffd70901103b3f06a98aeab695c6193 /src/xz/sandbox.c | |
parent | Adding debian version 5.6.1+really5.4.5-1. (diff) | |
download | xz-utils-25e43e389c26d56f90f2f20e3cee19c808b2a18c.tar.xz xz-utils-25e43e389c26d56f90f2f20e3cee19c808b2a18c.zip |
Merging upstream version 5.6.2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/xz/sandbox.c')
-rw-r--r-- | src/xz/sandbox.c | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/src/xz/sandbox.c b/src/xz/sandbox.c new file mode 100644 index 0000000..5bd2273 --- /dev/null +++ b/src/xz/sandbox.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: 0BSD + +/////////////////////////////////////////////////////////////////////////////// +// +/// \file sandbox.c +/// \brief Sandbox support +// +// Author: Lasse Collin +// +/////////////////////////////////////////////////////////////////////////////// + +#include "private.h" + + +#ifndef ENABLE_SANDBOX + +// Prevent an empty translation unit when no sandboxing is supported. +typedef int dummy; + +#else + +/// If the conditions for strict sandboxing (described in main()) +/// have been met, sandbox_allow_strict() can be called to set this +/// variable to true. +static bool strict_sandbox_allowed = false; + + +extern void +sandbox_allow_strict(void) +{ + strict_sandbox_allowed = true; + return; +} + + +// Strict sandboxing prevents opening any files. This *tries* to ensure +// that any auxiliary files that might be required are already open. +// +// Returns true if strict sandboxing is allowed, false otherwise. +static bool +prepare_for_strict_sandbox(void) +{ + if (!strict_sandbox_allowed) + return false; + + 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); + + return true; +} + +#endif + + +#if defined(HAVE_PLEDGE) + +/////////////// +// pledge(2) // +/////////////// + +#include <unistd.h> + + +extern void +sandbox_init(void) +{ + if (pledge("stdio rpath wpath cpath fattr", "")) { + // gettext hasn't been initialized yet so + // there's no point to call it here. + message_fatal("Failed to enable the sandbox"); + } + + return; +} + + +extern void +sandbox_enable_read_only(void) +{ + // We will be opening files for reading but + // won't create or remove any files. + if (pledge("stdio rpath", "")) + message_fatal(_("Failed to enable the sandbox")); + + return; +} + + +extern void +sandbox_enable_strict_if_allowed(int src_fd lzma_attribute((__unused__)), + int pipe_event_fd lzma_attribute((__unused__)), + int pipe_write_fd lzma_attribute((__unused__))) +{ + if (!prepare_for_strict_sandbox()) + return; + + // All files that need to be opened have already been opened. + if (pledge("stdio", "")) + message_fatal(_("Failed to enable the sandbox")); + + return; +} + + +#elif defined(HAVE_LINUX_LANDLOCK) + +////////////// +// Landlock // +////////////// + +#include <linux/landlock.h> +#include <sys/syscall.h> +#include <sys/prctl.h> + + +// Highest Landlock ABI version supported by this file: +// - For ABI versions 1-3 we don't need anything from <linux/landlock.h> +// that isn't part of version 1. +// - For ABI version 4 we need the larger struct landlock_ruleset_attr +// with the handled_access_net member. That is bundled with the macros +// LANDLOCK_ACCESS_NET_BIND_TCP and LANDLOCK_ACCESS_NET_CONNECT_TCP. +#ifdef LANDLOCK_ACCESS_NET_BIND_TCP +# define LANDLOCK_ABI_MAX 4 +#else +# define LANDLOCK_ABI_MAX 3 +#endif + + +/// Landlock ABI version supported by the kernel +static int landlock_abi; + + +// The required_rights should have those bits set that must not be restricted. +// This function will then bitwise-and ~required_rights with a mask matching +// the Landlock ABI version, leaving only those bits set that are supported +// by the ABI and allowed to be restricted by the function argument. +static void +enable_landlock(uint64_t required_rights) +{ + assert(landlock_abi <= LANDLOCK_ABI_MAX); + + if (landlock_abi <= 0) + return; + + // 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> for ABI versions 1-3. + const struct landlock_ruleset_attr attr = { + .handled_access_fs = ~required_rights + & ((1ULL << (12 + my_min(3, landlock_abi))) - 1), +#if LANDLOCK_ABI_MAX >= 4 + .handled_access_net = landlock_abi < 4 ? 0 : + (LANDLOCK_ACCESS_NET_BIND_TCP + | LANDLOCK_ACCESS_NET_CONNECT_TCP), +#endif + }; + + const int ruleset_fd = syscall(SYS_landlock_create_ruleset, + &attr, sizeof(attr), 0U); + if (ruleset_fd < 0) + message_fatal(_("Failed to enable the sandbox")); + + // 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 prepare_for_strict_sandbox() + // 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 + // sandbox_init() so we don't do it here again. + if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0U) != 0) + message_fatal(_("Failed to enable the sandbox")); + + return; +} + + +extern void +sandbox_init(void) +{ + // Prevent the process from gaining new privileges. This must be done + // before landlock_restrict_self(2) but since we will never need new + // privileges, this call can be done here already. + // + // This is supported since Linux 3.5. Ignore the return value to + // keep compatibility with old kernels. landlock_restrict_self(2) + // will fail if the no_new_privs attribute isn't set, thus if prctl() + // fails here the error will still be detected when it matters. + (void)prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + + // Get the highest Landlock ABI version supported by the kernel. + landlock_abi = syscall(SYS_landlock_create_ruleset, + (void *)NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + + // The kernel might support a newer ABI than this file. + if (landlock_abi > LANDLOCK_ABI_MAX) + landlock_abi = LANDLOCK_ABI_MAX; + + // These are all in ABI version 1 already. We don't need truncate + // rights because files are created with open() using O_EXCL and + // without O_TRUNC. + // + // LANDLOCK_ACCESS_FS_READ_DIR is included here to get a clear error + // message if xz is given a directory name. Without this permission + // the message would be "Permission denied" but with this permission + // it's "Is a directory, skipping". It could be worked around with + // stat()/lstat() but just giving this permission is simpler and + // shouldn't make the sandbox much weaker in practice. + const uint64_t required_rights + = LANDLOCK_ACCESS_FS_WRITE_FILE + | LANDLOCK_ACCESS_FS_READ_FILE + | LANDLOCK_ACCESS_FS_READ_DIR + | LANDLOCK_ACCESS_FS_REMOVE_FILE + | LANDLOCK_ACCESS_FS_MAKE_REG; + + enable_landlock(required_rights); + return; +} + + +extern void +sandbox_enable_read_only(void) +{ + // We will be opening files for reading but + // won't create or remove any files. + const uint64_t required_rights + = LANDLOCK_ACCESS_FS_READ_FILE + | LANDLOCK_ACCESS_FS_READ_DIR; + enable_landlock(required_rights); + return; +} + + +extern void +sandbox_enable_strict_if_allowed(int src_fd lzma_attribute((__unused__)), + int pipe_event_fd lzma_attribute((__unused__)), + int pipe_write_fd lzma_attribute((__unused__))) +{ + if (!prepare_for_strict_sandbox()) + return; + + // Allow all restrictions that the kernel supports with the + // highest Landlock ABI version that the kernel or xz supports. + // + // NOTE: LANDLOCK_ACCESS_FS_READ_DIR isn't needed here because + // the only input file has already been opened. + enable_landlock(0); + return; +} + + +#elif defined(HAVE_CAP_RIGHTS_LIMIT) + +////////////// +// Capsicum // +////////////// + +#include <sys/capsicum.h> + + +extern void +sandbox_init(void) +{ + // Nothing to do. + return; +} + + +extern void +sandbox_enable_read_only(void) +{ + // Nothing to do. + return; +} + + +extern void +sandbox_enable_strict_if_allowed( + int src_fd, int pipe_event_fd, int pipe_write_fd) +{ + if (!prepare_for_strict_sandbox()) + return; + + // 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(pipe_event_fd, cap_rights_init(&rights, + CAP_EVENT))) + goto error; + + if (cap_rights_limit(pipe_write_fd, cap_rights_init(&rights, + CAP_WRITE))) + goto error; + + return; + +error: + // 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; + + message_fatal(_("Failed to enable the sandbox")); +} + +#endif |