summaryrefslogtreecommitdiffstats
path: root/security/sandbox/linux/Sandbox.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /security/sandbox/linux/Sandbox.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'security/sandbox/linux/Sandbox.cpp')
-rw-r--r--security/sandbox/linux/Sandbox.cpp693
1 files changed, 693 insertions, 0 deletions
diff --git a/security/sandbox/linux/Sandbox.cpp b/security/sandbox/linux/Sandbox.cpp
new file mode 100644
index 000000000..7f1182be9
--- /dev/null
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -0,0 +1,693 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Sandbox.h"
+
+#include "LinuxCapabilities.h"
+#include "LinuxSched.h"
+#include "SandboxBrokerClient.h"
+#include "SandboxChroot.h"
+#include "SandboxFilter.h"
+#include "SandboxInternal.h"
+#include "SandboxLogging.h"
+#include "SandboxUtil.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/SandboxInfo.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/dump_bpf.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#if defined(ANDROID)
+#include "sandbox/linux/system_headers/linux_ucontext.h"
+#endif
+
+#ifdef MOZ_ASAN
+// Copy libsanitizer declarations to avoid depending on ASAN headers.
+// See also bug 1081242 comment #4.
+extern "C" {
+namespace __sanitizer {
+// Win64 uses long long, but this is Linux.
+typedef signed long sptr;
+} // namespace __sanitizer
+
+typedef struct {
+ int coverage_sandboxed;
+ __sanitizer::sptr coverage_fd;
+ unsigned int coverage_max_block_size;
+} __sanitizer_sandbox_arguments;
+
+MOZ_IMPORT_API void
+__sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
+} // extern "C"
+#endif // MOZ_ASAN
+
+// Signal number used to enable seccomp on each thread.
+int gSeccompTsyncBroadcastSignum = 0;
+
+namespace mozilla {
+
+// This is initialized by SandboxSetCrashFunc().
+SandboxCrashFunc gSandboxCrashFunc;
+
+#ifdef MOZ_GMP_SANDBOX
+// For media plugins, we can start the sandbox before we dlopen the
+// module, so we have to pre-open the file and simulate the sandboxed
+// open().
+static SandboxOpenedFile gMediaPluginFile;
+#endif
+
+static UniquePtr<SandboxChroot> gChrootHelper;
+static void (*gChromiumSigSysHandler)(int, siginfo_t*, void*);
+
+// Test whether a ucontext, interpreted as the state after a syscall,
+// indicates the given error. See also sandbox::Syscall::PutValueInUcontext.
+static bool
+ContextIsError(const ucontext_t *aContext, int aError)
+{
+ // Avoid integer promotion warnings. (The unary addition makes
+ // the decltype not evaluate to a reference type.)
+ typedef decltype(+SECCOMP_RESULT(aContext)) reg_t;
+
+#ifdef __mips__
+ return SECCOMP_PARM4(aContext) != 0
+ && SECCOMP_RESULT(aContext) == static_cast<reg_t>(aError);
+#else
+ return SECCOMP_RESULT(aContext) == static_cast<reg_t>(-aError);
+#endif
+}
+
+/**
+ * This is the SIGSYS handler function. It delegates to the Chromium
+ * TrapRegistry handler (see InstallSigSysHandler, below) and, if the
+ * trap handler installed by the policy would fail with ENOSYS,
+ * crashes the process. This allows unintentional policy failures to
+ * be reported as crash dumps and fixed. It also logs information
+ * about the failed system call.
+ *
+ * Note that this could be invoked in parallel on multiple threads and
+ * that it could be in async signal context (e.g., intercepting an
+ * open() called from an async signal handler).
+ */
+static void
+SigSysHandler(int nr, siginfo_t *info, void *void_context)
+{
+ ucontext_t *ctx = static_cast<ucontext_t*>(void_context);
+ // This shouldn't ever be null, but the Chromium handler checks for
+ // that and refrains from crashing, so let's not crash release builds:
+ MOZ_DIAGNOSTIC_ASSERT(ctx);
+ if (!ctx) {
+ return;
+ }
+
+ // Save a copy of the context before invoking the trap handler,
+ // which will overwrite one or more registers with the return value.
+ ucontext_t savedCtx = *ctx;
+
+ gChromiumSigSysHandler(nr, info, ctx);
+ if (!ContextIsError(ctx, ENOSYS)) {
+ return;
+ }
+
+ pid_t pid = getpid();
+ unsigned long syscall_nr = SECCOMP_SYSCALL(&savedCtx);
+ unsigned long args[6];
+ args[0] = SECCOMP_PARM1(&savedCtx);
+ args[1] = SECCOMP_PARM2(&savedCtx);
+ args[2] = SECCOMP_PARM3(&savedCtx);
+ args[3] = SECCOMP_PARM4(&savedCtx);
+ args[4] = SECCOMP_PARM5(&savedCtx);
+ args[5] = SECCOMP_PARM6(&savedCtx);
+
+ // TODO, someday when this is enabled on MIPS: include the two extra
+ // args in the error message.
+ SANDBOX_LOG_ERROR("seccomp sandbox violation: pid %d, syscall %d,"
+ " args %d %d %d %d %d %d. Killing process.",
+ pid, syscall_nr,
+ args[0], args[1], args[2], args[3], args[4], args[5]);
+
+ // Bug 1017393: record syscall number somewhere useful.
+ info->si_addr = reinterpret_cast<void*>(syscall_nr);
+
+ gSandboxCrashFunc(nr, info, &savedCtx);
+ _exit(127);
+}
+
+/**
+ * This function installs the SIGSYS handler. This is slightly
+ * complicated because we want to use Chromium's handler to dispatch
+ * to specific trap handlers defined in the policy, but we also need
+ * the full original signal context to give to Breakpad for crash
+ * dumps. So we install Chromium's handler first, then retrieve its
+ * address so our replacement can delegate to it.
+ */
+static void
+InstallSigSysHandler(void)
+{
+ struct sigaction act;
+
+ // Ensure that the Chromium handler is installed.
+ Unused << sandbox::Trap::Registry();
+
+ // If the signal handling state isn't as expected, crash now instead
+ // of crashing later (and more confusingly) when SIGSYS happens.
+
+ if (sigaction(SIGSYS, nullptr, &act) != 0) {
+ MOZ_CRASH("Couldn't read old SIGSYS disposition");
+ }
+ if ((act.sa_flags & SA_SIGINFO) != SA_SIGINFO) {
+ MOZ_CRASH("SIGSYS not already set to a siginfo handler?");
+ }
+ MOZ_RELEASE_ASSERT(act.sa_sigaction);
+ gChromiumSigSysHandler = act.sa_sigaction;
+ act.sa_sigaction = SigSysHandler;
+ // Currently, SA_NODEFER should already be set by the Chromium code,
+ // but it's harmless to ensure that it's set:
+ MOZ_ASSERT(act.sa_flags & SA_NODEFER);
+ act.sa_flags |= SA_NODEFER;
+ if (sigaction(SIGSYS, &act, nullptr) < 0) {
+ MOZ_CRASH("Couldn't change SIGSYS disposition");
+ }
+}
+
+/**
+ * This function installs the syscall filter, a.k.a. seccomp. The
+ * aUseTSync flag indicates whether this should apply to all threads
+ * in the process -- which will fail if the kernel doesn't support
+ * that -- or only the current thread.
+ *
+ * SECCOMP_MODE_FILTER is the "bpf" mode of seccomp which allows
+ * to pass a bpf program (in our case, it contains a syscall
+ * whitelist).
+ *
+ * PR_SET_NO_NEW_PRIVS ensures that it is impossible to grant more
+ * syscalls to the process beyond this point (even after fork()), and
+ * prevents gaining capabilities (e.g., by exec'ing a setuid root
+ * program). The kernel won't allow seccomp-bpf without doing this,
+ * because otherwise it could be used for privilege escalation attacks.
+ *
+ * Returns false if the filter was already installed (see the
+ * PR_SET_NO_NEW_PRIVS rule in SandboxFilter.cpp). Crashes on any
+ * other error condition.
+ *
+ * @see SandboxInfo
+ * @see BroadcastSetThreadSandbox
+ */
+static bool MOZ_MUST_USE
+InstallSyscallFilter(const sock_fprog *aProg, bool aUseTSync)
+{
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ if (!aUseTSync && errno == ETXTBSY) {
+ return false;
+ }
+ SANDBOX_LOG_ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %s", strerror(errno));
+ MOZ_CRASH("prctl(PR_SET_NO_NEW_PRIVS)");
+ }
+
+ if (aUseTSync) {
+ if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
+ SECCOMP_FILTER_FLAG_TSYNC, aProg) != 0) {
+ SANDBOX_LOG_ERROR("thread-synchronized seccomp failed: %s",
+ strerror(errno));
+ MOZ_CRASH("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)");
+ }
+ } else {
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, (unsigned long)aProg, 0, 0)) {
+ SANDBOX_LOG_ERROR("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) failed: %s",
+ strerror(errno));
+ MOZ_CRASH("seccomp+tsync failed, but kernel supports tsync");
+ }
+ }
+ return true;
+}
+
+// Use signals for permissions that need to be set per-thread.
+// The communication channel from the signal handler back to the main thread.
+static mozilla::Atomic<int> gSetSandboxDone;
+// Pass the filter itself through a global.
+const sock_fprog* gSetSandboxFilter;
+
+// We have to dynamically allocate the signal number; see bug 1038900.
+// This function returns the first realtime signal currently set to
+// default handling (i.e., not in use), or 0 if none could be found.
+//
+// WARNING: if this function or anything similar to it (including in
+// external libraries) is used on multiple threads concurrently, there
+// will be a race condition.
+static int
+FindFreeSignalNumber()
+{
+ for (int signum = SIGRTMAX; signum >= SIGRTMIN; --signum) {
+ struct sigaction sa;
+
+ if (sigaction(signum, nullptr, &sa) == 0 &&
+ (sa.sa_flags & SA_SIGINFO) == 0 &&
+ sa.sa_handler == SIG_DFL) {
+ return signum;
+ }
+ }
+ return 0;
+}
+
+// Returns true if sandboxing was enabled, or false if sandboxing
+// already was enabled. Crashes if sandboxing could not be enabled.
+static bool
+SetThreadSandbox()
+{
+ return InstallSyscallFilter(gSetSandboxFilter, false);
+}
+
+static void
+SetThreadSandboxHandler(int signum)
+{
+ // The non-zero number sent back to the main thread indicates
+ // whether action was taken.
+ if (SetThreadSandbox()) {
+ gSetSandboxDone = 2;
+ } else {
+ gSetSandboxDone = 1;
+ }
+ // Wake up the main thread. See the FUTEX_WAIT call, below, for an
+ // explanation.
+ syscall(__NR_futex, reinterpret_cast<int*>(&gSetSandboxDone),
+ FUTEX_WAKE, 1);
+}
+
+static void
+EnterChroot()
+{
+ if (gChrootHelper) {
+ gChrootHelper->Invoke();
+ gChrootHelper = nullptr;
+ }
+}
+
+static void
+BroadcastSetThreadSandbox(const sock_fprog* aFilter)
+{
+ pid_t pid, tid, myTid;
+ DIR *taskdp;
+ struct dirent *de;
+
+ // This function does not own *aFilter, so this global needs to
+ // always be zeroed before returning.
+ gSetSandboxFilter = aFilter;
+
+ static_assert(sizeof(mozilla::Atomic<int>) == sizeof(int),
+ "mozilla::Atomic<int> isn't represented by an int");
+ pid = getpid();
+ myTid = syscall(__NR_gettid);
+ taskdp = opendir("/proc/self/task");
+ if (taskdp == nullptr) {
+ SANDBOX_LOG_ERROR("opendir /proc/self/task: %s\n", strerror(errno));
+ MOZ_CRASH();
+ }
+
+ EnterChroot();
+
+ // In case this races with a not-yet-deprivileged thread cloning
+ // itself, repeat iterating over all threads until we find none
+ // that are still privileged.
+ bool sandboxProgress;
+ do {
+ sandboxProgress = false;
+ // For each thread...
+ while ((de = readdir(taskdp))) {
+ char *endptr;
+ tid = strtol(de->d_name, &endptr, 10);
+ if (*endptr != '\0' || tid <= 0) {
+ // Not a task ID.
+ continue;
+ }
+ if (tid == myTid) {
+ // Drop this thread's privileges last, below, so we can
+ // continue to signal other threads.
+ continue;
+ }
+
+ MOZ_RELEASE_ASSERT(gSeccompTsyncBroadcastSignum != 0);
+
+ // Reset the futex cell and signal.
+ gSetSandboxDone = 0;
+ if (syscall(__NR_tgkill, pid, tid, gSeccompTsyncBroadcastSignum) != 0) {
+ if (errno == ESRCH) {
+ SANDBOX_LOG_ERROR("Thread %d unexpectedly exited.", tid);
+ // Rescan threads, in case it forked before exiting.
+ sandboxProgress = true;
+ continue;
+ }
+ SANDBOX_LOG_ERROR("tgkill(%d,%d): %s\n", pid, tid, strerror(errno));
+ MOZ_CRASH();
+ }
+ // It's unlikely, but if the thread somehow manages to exit
+ // after receiving the signal but before entering the signal
+ // handler, we need to avoid blocking forever.
+ //
+ // Using futex directly lets the signal handler send the wakeup
+ // from an async signal handler (pthread mutex/condvar calls
+ // aren't allowed), and to use a relative timeout that isn't
+ // affected by changes to the system clock (not possible with
+ // POSIX semaphores).
+ //
+ // If a thread doesn't respond within a reasonable amount of
+ // time, but still exists, we crash -- the alternative is either
+ // blocking forever or silently losing security, and it
+ // shouldn't actually happen.
+ static const int crashDelay = 10; // seconds
+ struct timespec timeLimit;
+ clock_gettime(CLOCK_MONOTONIC, &timeLimit);
+ timeLimit.tv_sec += crashDelay;
+ while (true) {
+ static const struct timespec futexTimeout = { 0, 10*1000*1000 }; // 10ms
+ // Atomically: if gSetSandboxDone == 0, then sleep.
+ if (syscall(__NR_futex, reinterpret_cast<int*>(&gSetSandboxDone),
+ FUTEX_WAIT, 0, &futexTimeout) != 0) {
+ if (errno != EWOULDBLOCK && errno != ETIMEDOUT && errno != EINTR) {
+ SANDBOX_LOG_ERROR("FUTEX_WAIT: %s\n", strerror(errno));
+ MOZ_CRASH();
+ }
+ }
+ // Did the handler finish?
+ if (gSetSandboxDone > 0) {
+ if (gSetSandboxDone == 2) {
+ sandboxProgress = true;
+ }
+ break;
+ }
+ // Has the thread ceased to exist?
+ if (syscall(__NR_tgkill, pid, tid, 0) != 0) {
+ if (errno == ESRCH) {
+ SANDBOX_LOG_ERROR("Thread %d unexpectedly exited.", tid);
+ }
+ // Rescan threads, in case it forked before exiting.
+ // Also, if it somehow failed in a way that wasn't ESRCH,
+ // and still exists, that will be handled on the next pass.
+ sandboxProgress = true;
+ break;
+ }
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ if (now.tv_sec > timeLimit.tv_sec ||
+ (now.tv_sec == timeLimit.tv_sec &&
+ now.tv_nsec > timeLimit.tv_nsec)) {
+ SANDBOX_LOG_ERROR("Thread %d unresponsive for %d seconds."
+ " Killing process.",
+ tid, crashDelay);
+ MOZ_CRASH();
+ }
+ }
+ }
+ rewinddir(taskdp);
+ } while (sandboxProgress);
+
+ void (*oldHandler)(int);
+ oldHandler = signal(gSeccompTsyncBroadcastSignum, SIG_DFL);
+ gSeccompTsyncBroadcastSignum = 0;
+ if (oldHandler != SetThreadSandboxHandler) {
+ // See the comment on FindFreeSignalNumber about race conditions.
+ SANDBOX_LOG_ERROR("handler for signal %d was changed to %p!",
+ gSeccompTsyncBroadcastSignum, oldHandler);
+ MOZ_CRASH();
+ }
+ Unused << closedir(taskdp);
+ // And now, deprivilege the main thread:
+ SetThreadSandbox();
+ gSetSandboxFilter = nullptr;
+}
+
+static void
+ApplySandboxWithTSync(sock_fprog* aFilter)
+{
+ EnterChroot();
+ // At this point we're committed to using tsync, because the signal
+ // broadcast workaround needs to access procfs. (Unless chroot
+ // isn't used... but this failure shouldn't happen in the first
+ // place, so let's not make extra special cases for it.)
+ if (!InstallSyscallFilter(aFilter, true)) {
+ MOZ_CRASH();
+ }
+}
+
+// Common code for sandbox startup.
+static void
+SetCurrentProcessSandbox(UniquePtr<sandbox::bpf_dsl::Policy> aPolicy)
+{
+ MOZ_ASSERT(gSandboxCrashFunc);
+
+ // Note: PolicyCompiler borrows the policy and registry for its
+ // lifetime, but does not take ownership of them.
+ sandbox::bpf_dsl::PolicyCompiler compiler(aPolicy.get(),
+ sandbox::Trap::Registry());
+ sandbox::CodeGen::Program program = compiler.Compile();
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ sandbox::bpf_dsl::DumpBPF::PrintProgram(program);
+ }
+
+ InstallSigSysHandler();
+
+#ifdef MOZ_ASAN
+ __sanitizer_sandbox_arguments asanArgs;
+ asanArgs.coverage_sandboxed = 1;
+ asanArgs.coverage_fd = -1;
+ asanArgs.coverage_max_block_size = 0;
+ __sanitizer_sandbox_on_notify(&asanArgs);
+#endif
+
+ // The syscall takes a C-style array, so copy the vector into one.
+ size_t programLen = program.size();
+ UniquePtr<sock_filter[]> flatProgram(new sock_filter[programLen]);
+ for (auto i = program.begin(); i != program.end(); ++i) {
+ flatProgram[i - program.begin()] = *i;
+ }
+
+ sock_fprog fprog;
+ fprog.filter = flatProgram.get();
+ fprog.len = static_cast<unsigned short>(programLen);
+ MOZ_RELEASE_ASSERT(static_cast<size_t>(fprog.len) == programLen);
+
+ const SandboxInfo info = SandboxInfo::Get();
+ if (info.Test(SandboxInfo::kHasSeccompTSync)) {
+ if (info.Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG_ERROR("using seccomp tsync");
+ }
+ ApplySandboxWithTSync(&fprog);
+ } else {
+ if (info.Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG_ERROR("no tsync support; using signal broadcast");
+ }
+ BroadcastSetThreadSandbox(&fprog);
+ }
+ MOZ_RELEASE_ASSERT(!gChrootHelper, "forgot to chroot");
+}
+
+void
+SandboxEarlyInit(GeckoProcessType aType)
+{
+ const SandboxInfo info = SandboxInfo::Get();
+ if (info.Test(SandboxInfo::kUnexpectedThreads)) {
+ return;
+ }
+ MOZ_RELEASE_ASSERT(IsSingleThreaded());
+
+ // Which kinds of resource isolation (of those that need to be set
+ // up at this point) can be used by this process?
+ bool canChroot = false;
+ bool canUnshareNet = false;
+ bool canUnshareIPC = false;
+
+ switch (aType) {
+ case GeckoProcessType_Default:
+ MOZ_ASSERT(false, "SandboxEarlyInit in parent process");
+ return;
+#ifdef MOZ_GMP_SANDBOX
+ case GeckoProcessType_GMPlugin:
+ if (!info.Test(SandboxInfo::kEnabledForMedia)) {
+ break;
+ }
+ canUnshareNet = true;
+ canUnshareIPC = true;
+ // Need seccomp-bpf to intercept open().
+ canChroot = info.Test(SandboxInfo::kHasSeccompBPF);
+ break;
+#endif
+ // In the future, content processes will be able to use some of
+ // these.
+ default:
+ // Other cases intentionally left blank.
+ break;
+ }
+
+ // If TSYNC is not supported, set up signal handler
+ // used to enable seccomp on each thread.
+ if (!info.Test(SandboxInfo::kHasSeccompTSync)) {
+ gSeccompTsyncBroadcastSignum = FindFreeSignalNumber();
+ if (gSeccompTsyncBroadcastSignum == 0) {
+ SANDBOX_LOG_ERROR("No available signal numbers!");
+ MOZ_CRASH();
+ }
+
+ void (*oldHandler)(int);
+ oldHandler = signal(gSeccompTsyncBroadcastSignum, SetThreadSandboxHandler);
+ if (oldHandler != SIG_DFL) {
+ // See the comment on FindFreeSignalNumber about race conditions.
+ SANDBOX_LOG_ERROR("signal %d in use by handler %p!\n",
+ gSeccompTsyncBroadcastSignum, oldHandler);
+ MOZ_CRASH();
+ }
+ }
+
+ // If there's nothing to do, then we're done.
+ if (!canChroot && !canUnshareNet && !canUnshareIPC) {
+ return;
+ }
+
+ {
+ LinuxCapabilities existingCaps;
+ if (existingCaps.GetCurrent() && existingCaps.AnyEffective()) {
+ SANDBOX_LOG_ERROR("PLEASE DO NOT RUN THIS AS ROOT. Strange things may"
+ " happen when capabilities are dropped.");
+ }
+ }
+
+ // If capabilities can't be gained, then nothing can be done.
+ if (!info.Test(SandboxInfo::kHasUserNamespaces)) {
+ // Drop any existing capabilities; unsharing the user namespace
+ // would implicitly drop them, so if we're running in a broken
+ // configuration where that would matter (e.g., running as root
+ // from a non-root-owned mode-0700 directory) this means it will
+ // break the same way on all kernels and be easier to troubleshoot.
+ LinuxCapabilities().SetCurrent();
+ return;
+ }
+
+ // The failure cases for the various unshares, and setting up the
+ // chroot helper, don't strictly need to be fatal -- but they also
+ // shouldn't fail on any reasonable system, so let's take the small
+ // risk of breakage over the small risk of quietly providing less
+ // security than we expect. (Unlike in SandboxInfo, this is in the
+ // child process, so crashing here isn't as severe a response to the
+ // unexpected.)
+ if (!UnshareUserNamespace()) {
+ SANDBOX_LOG_ERROR("unshare(CLONE_NEWUSER): %s", strerror(errno));
+ // If CanCreateUserNamespace (SandboxInfo.cpp) returns true, then
+ // the unshare shouldn't have failed.
+ MOZ_CRASH("unshare(CLONE_NEWUSER)");
+ }
+ // No early returns after this point! We need to drop the
+ // capabilities that were gained by unsharing the user namesapce.
+
+ if (canUnshareIPC && syscall(__NR_unshare, CLONE_NEWIPC) != 0) {
+ SANDBOX_LOG_ERROR("unshare(CLONE_NEWIPC): %s", strerror(errno));
+ MOZ_CRASH("unshare(CLONE_NEWIPC)");
+ }
+
+ if (canUnshareNet && syscall(__NR_unshare, CLONE_NEWNET) != 0) {
+ SANDBOX_LOG_ERROR("unshare(CLONE_NEWNET): %s", strerror(errno));
+ MOZ_CRASH("unshare(CLONE_NEWNET)");
+ }
+
+ if (canChroot) {
+ gChrootHelper = MakeUnique<SandboxChroot>();
+ if (!gChrootHelper->Prepare()) {
+ SANDBOX_LOG_ERROR("failed to set up chroot helper");
+ MOZ_CRASH("SandboxChroot::Prepare");
+ }
+ }
+
+ if (!LinuxCapabilities().SetCurrent()) {
+ SANDBOX_LOG_ERROR("dropping capabilities: %s", strerror(errno));
+ MOZ_CRASH("can't drop capabilities");
+ }
+}
+
+#ifdef MOZ_CONTENT_SANDBOX
+/**
+ * Starts the seccomp sandbox for a content process. Should be called
+ * only once, and before any potentially harmful content is loaded.
+ *
+ * Will normally make the process exit on failure.
+*/
+bool
+SetContentProcessSandbox(int aBrokerFd)
+{
+ if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForContent)) {
+ if (aBrokerFd >= 0) {
+ close(aBrokerFd);
+ }
+ return false;
+ }
+
+ // This needs to live until the process exits.
+ static Maybe<SandboxBrokerClient> sBroker;
+ if (aBrokerFd >= 0) {
+ sBroker.emplace(aBrokerFd);
+ }
+
+ SetCurrentProcessSandbox(GetContentSandboxPolicy(sBroker.ptrOr(nullptr)));
+ return true;
+}
+#endif // MOZ_CONTENT_SANDBOX
+
+#ifdef MOZ_GMP_SANDBOX
+/**
+ * Starts the seccomp sandbox for a media plugin process. Should be
+ * called only once, and before any potentially harmful content is
+ * loaded -- including the plugin itself, if it's considered untrusted.
+ *
+ * The file indicated by aFilePath, if non-null, can be open()ed
+ * read-only, once, after the sandbox starts; it should be the .so
+ * file implementing the not-yet-loaded plugin.
+ *
+ * Will normally make the process exit on failure.
+*/
+void
+SetMediaPluginSandbox(const char *aFilePath)
+{
+ if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForMedia)) {
+ return;
+ }
+
+ MOZ_ASSERT(!gMediaPluginFile.mPath);
+ if (aFilePath) {
+ gMediaPluginFile.mPath = strdup(aFilePath);
+ gMediaPluginFile.mFd = open(aFilePath, O_RDONLY | O_CLOEXEC);
+ if (gMediaPluginFile.mFd == -1) {
+ SANDBOX_LOG_ERROR("failed to open plugin file %s: %s",
+ aFilePath, strerror(errno));
+ MOZ_CRASH();
+ }
+ } else {
+ gMediaPluginFile.mFd = -1;
+ }
+ // Finally, start the sandbox.
+ SetCurrentProcessSandbox(GetMediaSandboxPolicy(&gMediaPluginFile));
+}
+#endif // MOZ_GMP_SANDBOX
+
+} // namespace mozilla