diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /security/sandbox/linux/Sandbox.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-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.cpp | 693 |
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 |