]> git.ipfire.org Git - thirdparty/xz.git/commitdiff
xz: Move sandboxing code to sandbox.c and improve Landlock sandbox.
authorLasse Collin <lasse.collin@tukaani.org>
Sat, 17 Feb 2024 21:07:35 +0000 (23:07 +0200)
committerLasse Collin <lasse.collin@tukaani.org>
Sat, 17 Feb 2024 21:07:35 +0000 (23:07 +0200)
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.

CMakeLists.txt
src/xz/Makefile.am
src/xz/file_io.c
src/xz/file_io.h
src/xz/main.c
src/xz/private.h
src/xz/sandbox.c [new file with mode: 0644]
src/xz/sandbox.h [new file with mode: 0644]

index f30a82b66189e8b087cec60d9f1c597480df980c..96ff980b6c1b95287d84b018e446ee05f1d95920 100644 (file)
@@ -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
index 847405d7992a7ac5d6524b5ed91aaeb10779c3e1..4ff061f30c2aa6c3c656dcc19264764d7dd90765 100644 (file)
@@ -21,6 +21,8 @@ xz_SOURCES = \
        options.c \
        options.h \
        private.h \
+       sandbox.c \
+       sandbox.h \
        signals.c \
        signals.h \
        suffix.c \
index 876ee4de9ee79a38ac58a1ccc4858503f6250867..678a9a5ca8604a9a829c2a5ec740b07c8da196df 100644 (file)
@@ -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;
index b16a8faa948482df063700076c2c0136730e092c..ae7e2f38f52086239bc1637bb2bfb37c89b21b61 100644 (file)
@@ -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);
 
index 3d3d11d75be4cdb1ad15de7fc338e8f4d3d193fe..c3e81467765bd21bf4a95d3b956921867b118a08 100644 (file)
 #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.
index 0ab2ab4e3310290a8db9649e9c820b7588fbba1b..b370472e32c8887fecaf6ade19284747e1e32ddd 100644 (file)
 #      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 (file)
index 0000000..2c40db7
--- /dev/null
@@ -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 (file)
index 0000000..795c550
--- /dev/null
@@ -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);