aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac41
-rw-r--r--src/xz/Makefile.am2
-rw-r--r--src/xz/file_io.c81
-rw-r--r--src/xz/file_io.h6
-rw-r--r--src/xz/main.c18
-rw-r--r--src/xz/private.h4
6 files changed, 151 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index c8fdb5e4..aa68e33b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -468,6 +468,30 @@ AC_MSG_RESULT([$enable_symbol_versions])
AM_CONDITIONAL([COND_SYMVERS], [test "x$enable_symbol_versions" = xyes])
+##############
+# Sandboxing #
+##############
+
+AC_MSG_CHECKING([if sandboxing should be used])
+AC_ARG_ENABLE([sandbox], [AS_HELP_STRING([--enable-sandbox=METHOD],
+ [Sandboxing METHOD can be `auto', `no', or `capsicum'.
+ The default is `auto' which enables sandboxing if
+ a supported sandboxing method is found.])],
+ [], [enable_sandbox=auto])
+case $enable_sandbox in
+ auto)
+ AC_MSG_RESULT([maybe (autodetect)])
+ ;;
+ no | capsicum)
+ AC_MSG_RESULT([$enable_sandbox])
+ ;;
+ *)
+ AC_MSG_RESULT([])
+ AC_MSG_ERROR([--enable-sandbox only accepts `auto', `no', or `capsicum'.])
+ ;;
+esac
+
+
###############################################################################
# Checks for programs.
###############################################################################
@@ -698,6 +722,23 @@ AC_CHECK_DECL([_mm_movemask_epi8],
#include <immintrin.h>
#endif])
+# Check for sandbox support. If one is found, set enable_sandbox=found.
+case $enable_sandbox in
+ auto | capsicum)
+ AX_CHECK_CAPSICUM([enable_sandbox=found], [:])
+ ;;
+esac
+
+# If a specific sandboxing method was explicitly requested and it wasn't
+# found, give an error.
+case $enable_sandbox in
+ auto | no | found)
+ ;;
+ *)
+ AC_MSG_ERROR([$enable_sandbox support not found])
+ ;;
+esac
+
###############################################################################
# If using GCC, set some additional AM_CFLAGS:
diff --git a/src/xz/Makefile.am b/src/xz/Makefile.am
index 1f4a2791..500be411 100644
--- a/src/xz/Makefile.am
+++ b/src/xz/Makefile.am
@@ -49,7 +49,7 @@ xz_CPPFLAGS = \
-I$(top_srcdir)/src/liblzma/api \
-I$(top_builddir)/lib
-xz_LDADD = $(top_builddir)/src/liblzma/liblzma.la
+xz_LDADD = $(top_builddir)/src/liblzma/liblzma.la $(CAPSICUM_LIB)
if COND_GNULIB
xz_LDADD += $(top_builddir)/lib/libgnu.a
diff --git a/src/xz/file_io.c b/src/xz/file_io.c
index 20f512a2..308fa1d2 100644
--- a/src/xz/file_io.c
+++ b/src/xz/file_io.c
@@ -27,6 +27,14 @@ static bool warn_fchown;
# include <utime.h>
#endif
+#ifdef HAVE_CAPSICUM
+# ifdef HAVE_SYS_CAPSICUM_H
+# include <sys/capsicum.h>
+# else
+# include <sys/capability.h>
+# endif
+#endif
+
#include "tuklib_open_stdxxx.h"
#ifndef O_BINARY
@@ -48,6 +56,11 @@ 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().
@@ -139,6 +152,69 @@ 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) {
+ 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_CAPSICUM
+ // Capsicum needs FreeBSD 10.0 or later.
+ cap_rights_t rights;
+
+ if (cap_rights_limit(src_fd, cap_rights_init(&rights,
+ CAP_EVENT, CAP_FCNTL, CAP_LOOKUP, CAP_READ, CAP_SEEK)))
+ 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(user_abort_pipe[1], cap_rights_init(&rights,
+ CAP_EVENT, CAP_WRITE)))
+ goto error;
+
+ if (cap_enter())
+ goto error;
+
+#else
+# error ENABLE_SANDBOX is defined but no sandboxing method was found.
+#endif
+
+ message(V_DEBUG, _("Sandbox was successfully enabled"));
+ return;
+
+error:
+ message(V_DEBUG, _("Failed to enable the sandbox"));
+}
+#endif // ENABLE_SANDBOX
+
+
#ifndef TUKLIB_DOSLIKE
/// \brief Waits for input or output to become available or for a signal
///
@@ -656,6 +732,11 @@ io_open_src(const char *src_name)
const bool error = io_open_src_real(&pair);
signals_unblock();
+#ifdef ENABLE_SANDBOX
+ if (!error)
+ io_sandbox_enter(pair.src_fd);
+#endif
+
return error ? NULL : &pair;
}
diff --git a/src/xz/file_io.h b/src/xz/file_io.h
index 2de33792..6722aef8 100644
--- a/src/xz/file_io.h
+++ b/src/xz/file_io.h
@@ -80,6 +80,12 @@ 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 5608229d..5e0789ad 100644
--- a/src/xz/main.c
+++ b/src/xz/main.c
@@ -205,6 +205,24 @@ main(int argc, char **argv)
if (opt_mode != MODE_LIST)
signals_init();
+#ifdef ENABLE_SANDBOX
+ // Set a flag that 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
+ // or --list was used. Note that --test implies opt_stdout = true
+ // but --list doesn't.
+ //
+ // This is obviously not ideal but it was easy to implement and
+ // it covers the most common use cases.
+ //
+ // TODO: Make sandboxing work for other situations too.
+ if (args.files_name == NULL && args.arg_count == 1
+ && (opt_stdout || strcmp("-", args.arg_names[0]) == 0
+ || opt_mode == MODE_LIST))
+ io_allow_sandbox();
+#endif
+
// coder_run() handles compression, decompression, and testing.
// list_file() is for --list.
void (*run)(const char *filename) = opt_mode == MODE_LIST
diff --git a/src/xz/private.h b/src/xz/private.h
index 4acfa8dc..b84cbbb1 100644
--- a/src/xz/private.h
+++ b/src/xz/private.h
@@ -45,6 +45,10 @@
# define STDERR_FILENO (fileno(stderr))
#endif
+#ifdef HAVE_CAPSICUM
+# define ENABLE_SANDBOX 1
+#endif
+
#include "main.h"
#include "mytime.h"
#include "coder.h"