aboutsummaryrefslogtreecommitdiff
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
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.
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/xz/Makefile.am2
-rw-r--r--src/xz/file_io.c170
-rw-r--r--src/xz/file_io.h6
-rw-r--r--src/xz/main.c50
-rw-r--r--src/xz/private.h6
-rw-r--r--src/xz/sandbox.c295
-rw-r--r--src/xz/sandbox.h39
8 files changed, 357 insertions, 213 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f30a82b6..96ff980b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1393,6 +1393,8 @@ if(NOT MSVC OR MSVC_VERSION GREATER_EQUAL 1900)
src/xz/options.c
src/xz/options.h
src/xz/private.h
+ src/xz/sandbox.c
+ src/xz/sandbox.h
src/xz/signals.c
src/xz/signals.h
src/xz/suffix.c
diff --git a/src/xz/Makefile.am b/src/xz/Makefile.am
index 847405d7..4ff061f3 100644
--- a/src/xz/Makefile.am
+++ b/src/xz/Makefile.am
@@ -21,6 +21,8 @@ xz_SOURCES = \
options.c \
options.h \
private.h \
+ sandbox.c \
+ sandbox.h \
signals.c \
signals.h \
suffix.c \
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;
diff --git a/src/xz/file_io.h b/src/xz/file_io.h
index b16a8faa..ae7e2f38 100644
--- a/src/xz/file_io.h
+++ b/src/xz/file_io.h
@@ -99,12 +99,6 @@ extern void io_write_to_user_abort_pipe(void);
extern void io_no_sparse(void);
-#ifdef ENABLE_SANDBOX
-/// \brief main() calls this if conditions for sandboxing have been met.
-extern void io_allow_sandbox(void);
-#endif
-
-
/// \brief Open the source file
extern file_pair *io_open_src(const char *src_name);
diff --git a/src/xz/main.c b/src/xz/main.c
index 3d3d11d7..c3e81467 100644
--- a/src/xz/main.c
+++ b/src/xz/main.c
@@ -12,12 +12,6 @@
#include "private.h"
#include <ctype.h>
-// prctl(PR_SET_NO_NEW_PRIVS, ...) is required with Landlock but it can be
-// activated even when conditions for strict sandboxing aren't met.
-#ifdef HAVE_LINUX_LANDLOCK_H
-# include <sys/prctl.h>
-#endif
-
/// Exit status to use. This can be changed with set_exit_status().
static enum exit_status_type exit_status = E_SUCCESS;
@@ -148,32 +142,6 @@ read_name(const args_info *args)
int
main(int argc, char **argv)
{
-#ifdef HAVE_PLEDGE
- // OpenBSD's pledge(2) sandbox
- //
- // Unconditionally enable sandboxing with fairly relaxed promises.
- // This is still way better than having no sandbox at all. :-)
- // More strict promises will be made later in file_io.c if possible.
- if (pledge("stdio rpath wpath cpath fattr", "")) {
- // Don't translate the string or use message_fatal() as
- // those haven't been initialized yet.
- fprintf(stderr, "%s: Failed to enable the sandbox\n", argv[0]);
- return E_ERROR;
- }
-#endif
-
-#ifdef HAVE_LINUX_LANDLOCK_H
- // Prevent the process from gaining new privileges. This must be done
- // before landlock_restrict_self(2) in file_io.c 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);
-#endif
-
#if defined(_WIN32) && !defined(__CYGWIN__)
InitializeCriticalSection(&exit_status_cs);
#endif
@@ -187,6 +155,20 @@ main(int argc, char **argv)
// even indirectly like locale and gettext initializations.
io_init();
+#ifdef ENABLE_SANDBOX
+ // Enable such sandboxing that can always be enabled.
+ // This requires that progname has been set up.
+ // It's also good that io_init() has been called because it
+ // might need to do things that the initial sandbox won't allow.
+ // Otherwise this should be called as early as possible.
+ //
+ // NOTE: Calling this before tuklib_gettext_init() means that
+ // translated error message won't be available if sandbox
+ // initialization fails. However, sandbox_init() shouldn't
+ // fail and this order simply feels better.
+ sandbox_init();
+#endif
+
// Set up the locale and message translations.
tuklib_gettext_init(PACKAGE, LOCALEDIR);
@@ -241,7 +223,7 @@ main(int argc, char **argv)
signals_init();
#ifdef ENABLE_SANDBOX
- // Set a flag that sandboxing is allowed if all these are true:
+ // Set a flag that strict sandboxing is allowed if all these are true:
// - --files or --files0 wasn't used.
// - There is exactly one input file or we are reading from stdin.
// - We won't create any files: output goes to stdout or --test
@@ -255,7 +237,7 @@ main(int argc, char **argv)
if (args.files_name == NULL && args.arg_count == 1
&& (opt_stdout || strcmp("-", args.arg_names[0]) == 0
|| opt_mode == MODE_LIST))
- io_allow_sandbox();
+ sandbox_allow_strict();
#endif
// coder_run() handles compression, decompression, and testing.
diff --git a/src/xz/private.h b/src/xz/private.h
index 0ab2ab4e..b370472e 100644
--- a/src/xz/private.h
+++ b/src/xz/private.h
@@ -51,11 +51,6 @@
# define STDERR_FILENO (fileno(stderr))
#endif
-#if defined(HAVE_CAP_RIGHTS_LIMIT) || defined(HAVE_PLEDGE) \
- || defined(HAVE_LINUX_LANDLOCK_H)
-# define ENABLE_SANDBOX 1
-#endif
-
// Handling SIGTSTP keeps time-keeping for progress indicator correct
// if xz is stopped. It requires use of clock_gettime() as that is
// async-signal safe in POSIX. Require also SIGALRM support since
@@ -75,6 +70,7 @@
#include "hardware.h"
#include "file_io.h"
#include "options.h"
+#include "sandbox.h"
#include "signals.h"
#include "suffix.h"
#include "util.h"
diff --git a/src/xz/sandbox.c b/src/xz/sandbox.c
new file mode 100644
index 00000000..2c40db71
--- /dev/null
+++ b/src/xz/sandbox.c
@@ -0,0 +1,295 @@
+// 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_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;
+
+ if (pledge("stdio", ""))
+ message_fatal(_("Failed to enable the sandbox"));
+
+ return;
+}
+
+
+#elif defined(HAVE_LINUX_LANDLOCK_H)
+
+//////////////
+// Landlock //
+//////////////
+
+#include <linux/landlock.h>
+#include <sys/syscall.h>
+#include <sys/prctl.h>
+
+
+// Highest Landlock ABI version supported by this file
+#define LANDLOCK_ABI_MAX 3
+
+/// 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>.
+ const struct landlock_ruleset_attr attr = {
+ .handled_access_fs = ((1ULL << (12 + landlock_abi)) - 1)
+ & ~required_rights,
+ };
+
+ 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.
+ const uint64_t required_rights
+ = LANDLOCK_ACCESS_FS_WRITE_FILE
+ | LANDLOCK_ACCESS_FS_READ_FILE
+ | LANDLOCK_ACCESS_FS_REMOVE_FILE
+ | LANDLOCK_ACCESS_FS_MAKE_REG;
+
+ 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.
+ 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_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(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;
+
+ 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
diff --git a/src/xz/sandbox.h b/src/xz/sandbox.h
new file mode 100644
index 00000000..795c550f
--- /dev/null
+++ b/src/xz/sandbox.h
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file sandbox.h
+/// \brief Sandbox support
+//
+// Author: Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined(HAVE_PLEDGE) || defined(HAVE_LINUX_LANDLOCK_H) \
+ || defined(HAVE_CAP_RIGHTS_LIMIT)
+# define ENABLE_SANDBOX 1
+#endif
+
+
+/// \brief Enables early sandboxing that can always be enabled
+///
+/// This requires that tuklib_progname() and io_init() have been called.
+extern void sandbox_init(void);
+
+
+/// \brief Tell sandboxing code that strict sandboxing can be used
+///
+/// This function only sets a flag which will be read by
+/// sandbox_enable_strict_if_allowed().
+extern void sandbox_allow_strict(void);
+
+
+/// \brief Enable sandboxing that allows reading from one file
+///
+/// This does nothing if sandbox_allow_strict() hasn't been called.
+///
+/// \param src_fd File descriptor open for reading
+/// \param pipe_event_fd user_abort_pipe[0] from file_io.c
+/// \param pipe_write_fd user_abort_pipe[1] from file_io.c
+extern void sandbox_enable_strict_if_allowed(
+ int src_fd, int pipe_event_fd, int pipe_write_fd);