diff options
Diffstat (limited to 'security/sandbox/linux')
36 files changed, 0 insertions, 4992 deletions
diff --git a/security/sandbox/linux/LinuxCapabilities.cpp b/security/sandbox/linux/LinuxCapabilities.cpp deleted file mode 100644 index 87e24a009..000000000 --- a/security/sandbox/linux/LinuxCapabilities.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- 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 "LinuxCapabilities.h" - -#include <unistd.h> -#include <sys/syscall.h> - -namespace mozilla { - -bool -LinuxCapabilities::GetCurrent() { - __user_cap_header_struct header = { _LINUX_CAPABILITY_VERSION_3, 0 }; - return syscall(__NR_capget, &header, &mBits) == 0 - && header.version == _LINUX_CAPABILITY_VERSION_3; -} - -bool -LinuxCapabilities::SetCurrentRaw() const { - __user_cap_header_struct header = { _LINUX_CAPABILITY_VERSION_3, 0 }; - return syscall(__NR_capset, &header, &mBits) == 0 - && header.version == _LINUX_CAPABILITY_VERSION_3; -} - -} // namespace mozilla diff --git a/security/sandbox/linux/LinuxCapabilities.h b/security/sandbox/linux/LinuxCapabilities.h deleted file mode 100644 index 9d3220841..000000000 --- a/security/sandbox/linux/LinuxCapabilities.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_LinuxCapabilities_h -#define mozilla_LinuxCapabilities_h - -#include <linux/capability.h> -#include <stdint.h> - -#include "mozilla/Assertions.h" -#include "mozilla/Attributes.h" -#include "mozilla/PodOperations.h" - -// This class is a relatively simple interface to manipulating the -// capabilities of a Linux process/thread; see the capabilities(7) man -// page for background information. - -// Unfortunately, Android's kernel headers omit some definitions -// needed for the low-level capability interface. They're part of the -// stable syscall ABI, so it's safe to include them here. -#ifndef _LINUX_CAPABILITY_VERSION_3 -#define _LINUX_CAPABILITY_VERSION_3 0x20080522 -#define _LINUX_CAPABILITY_U32S_3 2 -#endif -#ifndef CAP_TO_INDEX -#define CAP_TO_INDEX(x) ((x) >> 5) -#define CAP_TO_MASK(x) (1 << ((x) & 31)) -#endif - -namespace mozilla { - -class LinuxCapabilities final -{ -public: - // A class to represent a bit within the capability sets as an lvalue. - class BitRef { - __u32& mWord; - __u32 mMask; - friend class LinuxCapabilities; - BitRef(__u32& aWord, uint32_t aMask) : mWord(aWord), mMask(aMask) { } - BitRef(const BitRef& aBit) : mWord(aBit.mWord), mMask(aBit.mMask) { } - public: - MOZ_IMPLICIT operator bool() const { - return mWord & mMask; - } - BitRef& operator=(bool aSetTo) { - if (aSetTo) { - mWord |= mMask; - } else { - mWord &= mMask; - } - return *this; - } - }; - - // The default value is the empty set. - LinuxCapabilities() { PodArrayZero(mBits); } - - // Get the current thread's capability sets and assign them to this - // object. Returns whether it succeeded and sets errno on failure. - // Shouldn't fail unless the kernel is very old. - bool GetCurrent(); - - // Try to set the current thread's capability sets to those - // specified in this object. Returns whether it succeeded and sets - // errno on failure. - bool SetCurrentRaw() const; - - // The capability model requires that the permitted set always be a - // superset of the effective and inheritable sets. This method - // expands the permitted set as needed and then sets the current - // thread's capabilities, as described above. - bool SetCurrent() { - Normalize(); - return SetCurrentRaw(); - } - - void Normalize() { - for (size_t i = 0; i < _LINUX_CAPABILITY_U32S_3; ++i) { - mBits[i].permitted |= mBits[i].effective | mBits[i].inheritable; - } - } - - bool AnyEffective() const { - for (size_t i = 0; i < _LINUX_CAPABILITY_U32S_3; ++i) { - if (mBits[i].effective != 0) { - return true; - } - } - return false; - } - - // These three methods expose individual bits in the three - // capability sets as objects that can be used as bool lvalues. - // The argument is the capability number, as defined in - // the <linux/capability.h> header. - BitRef Effective(unsigned aCap) - { - return GenericBitRef(&__user_cap_data_struct::effective, aCap); - } - - BitRef Permitted(unsigned aCap) - { - return GenericBitRef(&__user_cap_data_struct::permitted, aCap); - } - - BitRef Inheritable(unsigned aCap) - { - return GenericBitRef(&__user_cap_data_struct::inheritable, aCap); - } - -private: - __user_cap_data_struct mBits[_LINUX_CAPABILITY_U32S_3]; - - BitRef GenericBitRef(__u32 __user_cap_data_struct::* aField, unsigned aCap) - { - // Please don't pass untrusted data as the capability number. - MOZ_ASSERT(CAP_TO_INDEX(aCap) < _LINUX_CAPABILITY_U32S_3); - return BitRef(mBits[CAP_TO_INDEX(aCap)].*aField, CAP_TO_MASK(aCap)); - } -}; - -} // namespace mozilla - -#endif // mozilla_LinuxCapabilities_h diff --git a/security/sandbox/linux/LinuxSched.h b/security/sandbox/linux/LinuxSched.h deleted file mode 100644 index 148067d34..000000000 --- a/security/sandbox/linux/LinuxSched.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_LinuxSched_h -#define mozilla_LinuxSched_h - -#include <linux/sched.h> - -// Some build environments, in particular the Android NDK, don't -// define some of the newer clone/unshare flags ("newer" relatively -// speaking; CLONE_NEWUTS is present since kernel 2.6.19 in 2006). - -#ifndef CLONE_NEWUTS -#define CLONE_NEWUTS 0x04000000 -#endif -#ifndef CLONE_NEWIPC -#define CLONE_NEWIPC 0x08000000 -#endif -#ifndef CLONE_NEWUSER -#define CLONE_NEWUSER 0x10000000 -#endif -#ifndef CLONE_NEWPID -#define CLONE_NEWPID 0x20000000 -#endif -#ifndef CLONE_NEWNET -#define CLONE_NEWNET 0x40000000 -#endif -#ifndef CLONE_IO -#define CLONE_IO 0x80000000 -#endif - -#endif diff --git a/security/sandbox/linux/Sandbox.cpp b/security/sandbox/linux/Sandbox.cpp deleted file mode 100644 index 80a18f855..000000000 --- a/security/sandbox/linux/Sandbox.cpp +++ /dev/null @@ -1,609 +0,0 @@ -/* -*- 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; - -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; - 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"); - } -} - -} // namespace mozilla diff --git a/security/sandbox/linux/Sandbox.h b/security/sandbox/linux/Sandbox.h deleted file mode 100644 index 9d1c3d4b3..000000000 --- a/security/sandbox/linux/Sandbox.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_Sandbox_h -#define mozilla_Sandbox_h - -#include "mozilla/Types.h" -#include "nsXULAppAPI.h" - -// This defines the entry points for a content process to start -// sandboxing itself. See also SandboxInfo.h for what parts of -// sandboxing are enabled/supported. - -namespace mozilla { - -// This must be called early, while the process is still single-threaded. -MOZ_EXPORT void SandboxEarlyInit(GeckoProcessType aType); - -} // namespace mozilla - -#endif // mozilla_Sandbox_h diff --git a/security/sandbox/linux/SandboxBrokerClient.cpp b/security/sandbox/linux/SandboxBrokerClient.cpp deleted file mode 100644 index 68744ad02..000000000 --- a/security/sandbox/linux/SandboxBrokerClient.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* -*- 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 "SandboxBrokerClient.h" -#include "SandboxInfo.h" -#include "SandboxLogging.h" - -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "mozilla/Assertions.h" -#include "mozilla/NullPtr.h" -#include "base/strings/safe_sprintf.h" - -namespace mozilla { - -SandboxBrokerClient::SandboxBrokerClient(int aFd) -: mFileDesc(aFd) -{ -} - -SandboxBrokerClient::~SandboxBrokerClient() -{ - close(mFileDesc); -} - -int -SandboxBrokerClient::DoCall(const Request* aReq, const char* aPath, - const char* aPath2, void* aResponseBuff, - bool expectFd) -{ - // Remap /proc/self to the actual pid, so that the broker can open - // it. This happens here instead of in the broker to follow the - // principle of least privilege and keep the broker as simple as - // possible. (Note: when pid namespaces happen, this will also need - // to remap the inner pid to the outer pid.) - // We only remap the first path. - static const char kProcSelf[] = "/proc/self/"; - static const size_t kProcSelfLen = sizeof(kProcSelf) - 1; - const char* path = aPath; - // This buffer just needs to be large enough for any such path that - // the policy would actually allow. sizeof("/proc/2147483647/") == 18. - char rewrittenPath[64]; - if (strncmp(aPath, kProcSelf, kProcSelfLen) == 0) { - ssize_t len = - base::strings::SafeSPrintf(rewrittenPath, "/proc/%d/%s", - getpid(), aPath + kProcSelfLen); - if (static_cast<size_t>(len) < sizeof(rewrittenPath)) { - if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { - SANDBOX_LOG_ERROR("rewriting %s -> %s", aPath, rewrittenPath); - } - path = rewrittenPath; - } else { - SANDBOX_LOG_ERROR("not rewriting unexpectedly long path %s", aPath); - } - } - - struct iovec ios[3]; - int respFds[2]; - - // Set up iovecs for request + path. - ios[0].iov_base = const_cast<Request*>(aReq); - ios[0].iov_len = sizeof(*aReq); - ios[1].iov_base = const_cast<char*>(path); - ios[1].iov_len = strlen(path) + 1; - if (aPath2 != nullptr) { - ios[2].iov_base = const_cast<char*>(aPath2); - ios[2].iov_len = strlen(aPath2) + 1; - } else { - ios[2].iov_base = 0; - ios[2].iov_len = 0; - } - if (ios[1].iov_len > kMaxPathLen) { - return -ENAMETOOLONG; - } - if (ios[2].iov_len > kMaxPathLen) { - return -ENAMETOOLONG; - } - - // Create response socket and send request. - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, respFds) < 0) { - return -errno; - } - const ssize_t sent = SendWithFd(mFileDesc, ios, 3, respFds[1]); - const int sendErrno = errno; - MOZ_ASSERT(sent < 0 || - static_cast<size_t>(sent) == ios[0].iov_len - + ios[1].iov_len - + ios[2].iov_len); - close(respFds[1]); - if (sent < 0) { - close(respFds[0]); - return -sendErrno; - } - - // Set up iovecs for response. - Response resp; - ios[0].iov_base = &resp; - ios[0].iov_len = sizeof(resp); - if (aResponseBuff) { - ios[1].iov_base = aResponseBuff; - ios[1].iov_len = aReq->mBufSize; - } else { - ios[1].iov_base = nullptr; - ios[1].iov_len = 0; - } - - // Wait for response and return appropriately. - int openedFd = -1; - const ssize_t recvd = RecvWithFd(respFds[0], ios, aResponseBuff ? 2 : 1, - expectFd ? &openedFd : nullptr); - const int recvErrno = errno; - close(respFds[0]); - if (recvd < 0) { - return -recvErrno; - } - if (recvd == 0) { - SANDBOX_LOG_ERROR("Unexpected EOF, op %d flags 0%o path %s", - aReq->mOp, aReq->mFlags, path); - return -EIO; - } - MOZ_ASSERT(static_cast<size_t>(recvd) <= ios[0].iov_len + ios[1].iov_len); - // Some calls such as readlink return a size if successful - if (resp.mError >= 0) { - // Success! - if (expectFd) { - MOZ_ASSERT(openedFd >= 0); - return openedFd; - } - return resp.mError; - } - if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { - // Keep in mind that "rejected" files can include ones that don't - // actually exist, if it's something that's optional or part of a - // search path (e.g., shared libraries). In those cases, this - // error message is expected. - SANDBOX_LOG_ERROR("Rejected errno %d op %d flags 0%o path %s", - resp.mError, aReq->mOp, aReq->mFlags, path); - } - if (openedFd >= 0) { - close(openedFd); - } - return resp.mError; -} - -int -SandboxBrokerClient::Open(const char* aPath, int aFlags) -{ - Request req = { SANDBOX_FILE_OPEN, aFlags, 0 }; - int maybeFd = DoCall(&req, aPath, nullptr, nullptr, true); - if (maybeFd >= 0) { - // NSPR has opinions about file flags. Fix O_CLOEXEC. - if ((aFlags & O_CLOEXEC) == 0) { - fcntl(maybeFd, F_SETFD, 0); - } - } - return maybeFd; -} - -int -SandboxBrokerClient::Access(const char* aPath, int aMode) -{ - Request req = { SANDBOX_FILE_ACCESS, aMode, 0 }; - return DoCall(&req, aPath, nullptr, nullptr, false); -} - -int -SandboxBrokerClient::Stat(const char* aPath, statstruct* aStat) -{ - Request req = { SANDBOX_FILE_STAT, 0, sizeof(statstruct) }; - return DoCall(&req, aPath, nullptr, (void*)aStat, false); -} - -int -SandboxBrokerClient::LStat(const char* aPath, statstruct* aStat) -{ - Request req = { SANDBOX_FILE_STAT, O_NOFOLLOW, sizeof(statstruct) }; - return DoCall(&req, aPath, nullptr, (void*)aStat, false); -} - -int -SandboxBrokerClient::Chmod(const char* aPath, int aMode) -{ - Request req = {SANDBOX_FILE_CHMOD, aMode, 0}; - return DoCall(&req, aPath, nullptr, nullptr, false); -} - -int -SandboxBrokerClient::Link(const char* aOldPath, const char* aNewPath) -{ - Request req = {SANDBOX_FILE_LINK, 0, 0}; - return DoCall(&req, aOldPath, aNewPath, nullptr, false); -} - -int -SandboxBrokerClient::Symlink(const char* aOldPath, const char* aNewPath) -{ - Request req = {SANDBOX_FILE_SYMLINK, 0, 0}; - return DoCall(&req, aOldPath, aNewPath, nullptr, false); -} - -int -SandboxBrokerClient::Rename(const char* aOldPath, const char* aNewPath) -{ - Request req = {SANDBOX_FILE_RENAME, 0, 0}; - return DoCall(&req, aOldPath, aNewPath, nullptr, false); -} - -int -SandboxBrokerClient::Mkdir(const char* aPath, int aMode) -{ - Request req = {SANDBOX_FILE_MKDIR, aMode, 0}; - return DoCall(&req, aPath, nullptr, nullptr, false); -} - -int -SandboxBrokerClient::Unlink(const char* aPath) -{ - Request req = {SANDBOX_FILE_UNLINK, 0, 0}; - return DoCall(&req, aPath, nullptr, nullptr, false); -} - -int -SandboxBrokerClient::Rmdir(const char* aPath) -{ - Request req = {SANDBOX_FILE_RMDIR, 0, 0}; - return DoCall(&req, aPath, nullptr, nullptr, false); -} - -int -SandboxBrokerClient::Readlink(const char* aPath, void* aBuff, size_t aSize) -{ - Request req = {SANDBOX_FILE_READLINK, 0, aSize}; - return DoCall(&req, aPath, nullptr, aBuff, false); -} - -} // namespace mozilla - diff --git a/security/sandbox/linux/SandboxBrokerClient.h b/security/sandbox/linux/SandboxBrokerClient.h deleted file mode 100644 index 06db2f183..000000000 --- a/security/sandbox/linux/SandboxBrokerClient.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxBrokerClient_h -#define mozilla_SandboxBrokerClient_h - -#include "broker/SandboxBrokerCommon.h" -#include "broker/SandboxBrokerUtils.h" - -#include "mozilla/Attributes.h" - -// This is the client for the sandbox broker described in -// broker/SandboxBroker.h; its constructor takes the file descriptor -// returned by SandboxBroker::Create, passed to the child over IPC. -// -// The operations exposed here can be called from any thread and in -// async signal handlers, like the corresponding system calls. The -// intended use is from a seccomp-bpf SIGSYS handler, to transparently -// replace those syscalls, but they could also be used directly. - -struct stat; - -namespace mozilla { - -class SandboxBrokerClient final : private SandboxBrokerCommon { - public: - explicit SandboxBrokerClient(int aFd); - ~SandboxBrokerClient(); - - int Open(const char* aPath, int aFlags); - int Access(const char* aPath, int aMode); - int Stat(const char* aPath, statstruct* aStat); - int LStat(const char* aPath, statstruct* aStat); - int Chmod(const char* aPath, int aMode); - int Link(const char* aPath, const char* aPath2); - int Mkdir(const char* aPath, int aMode); - int Symlink(const char* aOldPath, const char* aNewPath); - int Rename(const char* aOldPath, const char* aNewPath); - int Unlink(const char* aPath); - int Rmdir(const char* aPath); - int Readlink(const char* aPath, void* aBuf, size_t aBufSize); - - private: - int mFileDesc; - - int DoCall(const Request* aReq, - const char* aPath, - const char* aPath2, - void *aReponseBuff, - bool expectFd); -}; - -} // namespace mozilla - -#endif // mozilla_SandboxBrokerClient_h diff --git a/security/sandbox/linux/SandboxChroot.cpp b/security/sandbox/linux/SandboxChroot.cpp deleted file mode 100644 index 2091bfe05..000000000 --- a/security/sandbox/linux/SandboxChroot.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/* -*- 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 "SandboxChroot.h" - -#include "SandboxLogging.h" -#include "LinuxCapabilities.h" - -#include <errno.h> -#include <fcntl.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "base/posix/eintr_wrapper.h" -#include "mozilla/Assertions.h" -#include "mozilla/DebugOnly.h" -#include "mozilla/NullPtr.h" - -#define MOZ_ALWAYS_ZERO(e) MOZ_ALWAYS_TRUE((e) == 0) - -namespace mozilla { - -SandboxChroot::SandboxChroot() -{ - pthread_mutexattr_t attr; - MOZ_ALWAYS_ZERO(pthread_mutexattr_init(&attr)); - MOZ_ALWAYS_ZERO(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)); - MOZ_ALWAYS_ZERO(pthread_mutex_init(&mMutex, &attr)); - MOZ_ALWAYS_ZERO(pthread_cond_init(&mWakeup, nullptr)); - mCommand = NO_THREAD; -} - -SandboxChroot::~SandboxChroot() -{ - SendCommand(JUST_EXIT); - MOZ_ALWAYS_ZERO(pthread_mutex_destroy(&mMutex)); - MOZ_ALWAYS_ZERO(pthread_cond_destroy(&mWakeup)); -} - -bool -SandboxChroot::SendCommand(Command aComm) -{ - MOZ_ALWAYS_ZERO(pthread_mutex_lock(&mMutex)); - if (mCommand == NO_THREAD) { - MOZ_RELEASE_ASSERT(aComm == JUST_EXIT); - MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex)); - return false; - } else { - MOZ_ASSERT(mCommand == NO_COMMAND); - mCommand = aComm; - MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex)); - MOZ_ALWAYS_ZERO(pthread_cond_signal(&mWakeup)); - void *retval; - if (pthread_join(mThread, &retval) != 0 || retval != nullptr) { - MOZ_CRASH("Failed to stop privileged chroot thread"); - } - MOZ_ASSERT(mCommand == NO_THREAD); - } - return true; -} - -static void -AlwaysClose(int fd) -{ - if (IGNORE_EINTR(close(fd)) != 0) { - SANDBOX_LOG_ERROR("close: %s", strerror(errno)); - MOZ_CRASH("failed to close()"); - } -} - -static int -OpenDeletedDirectory() -{ - // We don't need this directory to persist between invocations of - // the program (nor need it to be cleaned up if something goes wrong - // here, because mkdtemp will choose a fresh name), so /tmp as - // specified by FHS is adequate. - // - // However, this needs a filesystem where a deleted directory can - // still be used, and /tmp is sometimes not that; e.g., aufs(5), - // often used for containers, will cause the chroot() to fail with - // ESTALE (bug 1162965). So this uses /dev/shm if possible instead. - char tmpPath[] = "/tmp/mozsandbox.XXXXXX"; - char shmPath[] = "/dev/shm/mozsandbox.XXXXXX"; - char* path; - if (mkdtemp(shmPath)) { - path = shmPath; - } else if (mkdtemp(tmpPath)) { - path = tmpPath; - } else { - SANDBOX_LOG_ERROR("mkdtemp: %s", strerror(errno)); - return -1; - } - int fd = HANDLE_EINTR(open(path, O_RDONLY | O_DIRECTORY)); - if (fd < 0) { - SANDBOX_LOG_ERROR("open %s: %s", path, strerror(errno)); - // Try to clean up. Shouldn't fail, but livable if it does. - DebugOnly<bool> ok = HANDLE_EINTR(rmdir(path)) == 0; - MOZ_ASSERT(ok); - return -1; - } - if (HANDLE_EINTR(rmdir(path)) != 0) { - SANDBOX_LOG_ERROR("rmdir %s: %s", path, strerror(errno)); - AlwaysClose(fd); - return -1; - } - return fd; -} - -bool -SandboxChroot::Prepare() -{ - LinuxCapabilities caps; - if (!caps.GetCurrent() || !caps.Effective(CAP_SYS_CHROOT)) { - SANDBOX_LOG_ERROR("don't have permission to chroot"); - return false; - } - mFd = OpenDeletedDirectory(); - if (mFd < 0) { - SANDBOX_LOG_ERROR("failed to create empty directory for chroot"); - return false; - } - MOZ_ALWAYS_ZERO(pthread_mutex_lock(&mMutex)); - MOZ_ASSERT(mCommand == NO_THREAD); - if (pthread_create(&mThread, nullptr, StaticThreadMain, this) != 0) { - MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex)); - SANDBOX_LOG_ERROR("pthread_create: %s", strerror(errno)); - return false; - } - while (mCommand != NO_COMMAND) { - MOZ_ASSERT(mCommand == NO_THREAD); - MOZ_ALWAYS_ZERO(pthread_cond_wait(&mWakeup, &mMutex)); - } - MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex)); - return true; -} - -void -SandboxChroot::Invoke() -{ - MOZ_ALWAYS_TRUE(SendCommand(DO_CHROOT)); -} - -static bool -ChrootToFileDesc(int fd) -{ - if (fchdir(fd) != 0) { - SANDBOX_LOG_ERROR("fchdir: %s", strerror(errno)); - return false; - } - if (chroot(".") != 0) { - SANDBOX_LOG_ERROR("chroot: %s", strerror(errno)); - return false; - } - return true; -} - -/* static */ void* -SandboxChroot::StaticThreadMain(void* aVoidPtr) -{ - static_cast<SandboxChroot*>(aVoidPtr)->ThreadMain(); - return nullptr; -} - -void -SandboxChroot::ThreadMain() -{ - // First, drop everything that isn't CAP_SYS_CHROOT. (This code - // assumes that this thread already has effective CAP_SYS_CHROOT, - // because Prepare() checked for it before creating this thread.) - LinuxCapabilities caps; - caps.Effective(CAP_SYS_CHROOT) = true; - if (!caps.SetCurrent()) { - SANDBOX_LOG_ERROR("capset: %s", strerror(errno)); - MOZ_CRASH("Can't limit chroot thread's capabilities"); - } - - MOZ_ALWAYS_ZERO(pthread_mutex_lock(&mMutex)); - MOZ_ASSERT(mCommand == NO_THREAD); - mCommand = NO_COMMAND; - MOZ_ALWAYS_ZERO(pthread_cond_signal(&mWakeup)); - while (mCommand == NO_COMMAND) { - MOZ_ALWAYS_ZERO(pthread_cond_wait(&mWakeup, &mMutex)); - } - if (mCommand == DO_CHROOT) { - MOZ_ASSERT(mFd >= 0); - if (!ChrootToFileDesc(mFd)) { - MOZ_CRASH("Failed to chroot"); - } - } else { - MOZ_ASSERT(mCommand == JUST_EXIT); - } - if (mFd >= 0) { - AlwaysClose(mFd); - mFd = -1; - } - mCommand = NO_THREAD; - MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex)); - // Drop the remaining capabilities; see note in SandboxChroot.h - // about the potential unreliability of pthread_join. - if (!LinuxCapabilities().SetCurrent()) { - MOZ_CRASH("can't drop capabilities"); - } -} - -} // namespace mozilla - -#undef MOZ_ALWAYS_ZERO diff --git a/security/sandbox/linux/SandboxChroot.h b/security/sandbox/linux/SandboxChroot.h deleted file mode 100644 index 3ad89b732..000000000 --- a/security/sandbox/linux/SandboxChroot.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxChroot_h -#define mozilla_SandboxChroot_h - -#include <pthread.h> - -#include "mozilla/Attributes.h" - -// This class uses the chroot(2) system call and Linux namespaces to -// revoke the process's access to the filesystem. It requires that -// the process be able to create user namespaces; this is the -// kHasUserNamespaces in SandboxInfo.h. -// -// Usage: call Prepare() from a thread with CAP_SYS_CHROOT in its -// effective capability set, then later call Invoke() when ready to -// drop filesystem access. Prepare() creates a thread to do the -// chrooting, so the caller can (and should!) drop its own -// capabilities afterwards. When Invoke() returns, the thread will -// have exited. -// -// (Exception: on Android/B2G <= KitKat, because of how pthread_join -// is implemented, the thread may still exist, but it will not have -// capabilities. Accordingly, on such systems, be careful about -// namespaces or other resources the thread might have inherited.) -// -// Prepare() can fail (return false); for example, if it doesn't have -// CAP_SYS_CHROOT or if it can't create a directory to chroot into. -// -// The root directory will be empty and deleted, so the process will -// not be able to create new entries in it regardless of permissions. - -namespace mozilla { - -class SandboxChroot final { -public: - SandboxChroot(); - ~SandboxChroot(); - bool Prepare(); - void Invoke(); -private: - enum Command { - NO_THREAD, - NO_COMMAND, - DO_CHROOT, - JUST_EXIT, - }; - - pthread_t mThread; - pthread_mutex_t mMutex; - pthread_cond_t mWakeup; - Command mCommand; - int mFd; - - void ThreadMain(); - static void* StaticThreadMain(void* aVoidPtr); - bool SendCommand(Command aComm); -}; - -} // namespace mozilla - -#endif // mozilla_SandboxChroot_h diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp deleted file mode 100644 index afaf53cec..000000000 --- a/security/sandbox/linux/SandboxFilter.cpp +++ /dev/null @@ -1,343 +0,0 @@ -/* -*- 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 "SandboxFilter.h" -#include "SandboxFilterUtil.h" - -#include "SandboxBrokerClient.h" -#include "SandboxInfo.h" -#include "SandboxInternal.h" -#include "SandboxLogging.h" - -#include "mozilla/UniquePtr.h" - -#include <errno.h> -#include <fcntl.h> -#include <linux/ipc.h> -#include <linux/net.h> -#include <linux/prctl.h> -#include <linux/sched.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/socket.h> -#include <sys/syscall.h> -#include <time.h> -#include <unistd.h> - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/system_headers/linux_seccomp.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" - -using namespace sandbox::bpf_dsl; -#define CASES SANDBOX_BPF_DSL_CASES - -// Fill in defines in case of old headers. -// (Warning: these are wrong on PA-RISC.) -#ifndef MADV_NOHUGEPAGE -#define MADV_NOHUGEPAGE 15 -#endif -#ifndef MADV_DONTDUMP -#define MADV_DONTDUMP 16 -#endif - -// Added in Linux 4.5; see bug 1303813. -#ifndef MADV_FREE -#define MADV_FREE 8 -#endif - -#ifndef PR_SET_PTRACER -#define PR_SET_PTRACER 0x59616d61 -#endif - -// To avoid visual confusion between "ifdef ANDROID" and "ifndef ANDROID": -#ifndef ANDROID -#define DESKTOP -#endif - -// This file defines the seccomp-bpf system call filter policies. -// See also SandboxFilterUtil.h, for the CASES_FOR_* macros and -// SandboxFilterBase::Evaluate{Socket,Ipc}Call. -// -// One important difference from how Chromium bpf_dsl filters are -// normally interpreted: returning -ENOSYS from a Trap() handler -// indicates an unexpected system call; SigSysHandler() in Sandbox.cpp -// will detect this, request a crash dump, and terminate the process. -// This does not apply to using Error(ENOSYS) in the policy, so that -// can be used if returning an actual ENOSYS is needed. - -namespace mozilla { - -// This class whitelists everything used by the sandbox itself, by the -// core IPC code, by the crash reporter, or other core code. -class SandboxPolicyCommon : public SandboxPolicyBase -{ -protected: - typedef const sandbox::arch_seccomp_data& ArgsRef; - - static intptr_t BlockedSyscallTrap(ArgsRef aArgs, void *aux) { - MOZ_ASSERT(!aux); - return -ENOSYS; - } - -private: -#if defined(ANDROID) && ANDROID_VERSION < 16 - // Bug 1093893: Translate tkill to tgkill for pthread_kill; fixed in - // bionic commit 10c8ce59a (in JB and up; API level 16 = Android 4.1). - static intptr_t TKillCompatTrap(const sandbox::arch_seccomp_data& aArgs, - void *aux) - { - return syscall(__NR_tgkill, getpid(), aArgs.args[0], aArgs.args[1]); - } -#endif - - static intptr_t SetNoNewPrivsTrap(ArgsRef& aArgs, void* aux) { - if (gSetSandboxFilter == nullptr) { - // Called after BroadcastSetThreadSandbox finished, therefore - // not our doing and not expected. - return BlockedSyscallTrap(aArgs, nullptr); - } - // Signal that the filter is already in place. - return -ETXTBSY; - } - -public: - virtual ResultExpr InvalidSyscall() const override { - return Trap(BlockedSyscallTrap, nullptr); - } - - virtual ResultExpr ClonePolicy(ResultExpr failPolicy) const { - // Allow use for simple thread creation (pthread_create) only. - - // WARNING: s390 and cris pass the flags in the second arg -- see - // CLONE_BACKWARDS2 in arch/Kconfig in the kernel source -- but we - // don't support seccomp-bpf on those archs yet. - Arg<int> flags(0); - - // The glibc source hasn't changed the thread creation clone flags - // since 2004, so this *should* be safe to hard-code. Bionic's - // value has changed a few times, and has converged on the same one - // as glibc; allow any of them. - static const int flags_common = CLONE_VM | CLONE_FS | CLONE_FILES | - CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM; - static const int flags_modern = flags_common | CLONE_SETTLS | - CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; - - // Can't use CASES here because its decltype magic infers const - // int instead of regular int and bizarre voluminous errors issue - // forth from the depths of the standard library implementation. - return Switch(flags) -#ifdef ANDROID - .Case(flags_common | CLONE_DETACHED, Allow()) // <= JB 4.2 - .Case(flags_common, Allow()) // JB 4.3 or KK 4.4 -#endif - .Case(flags_modern, Allow()) // Android L or glibc - .Default(failPolicy); - } - - virtual ResultExpr PrctlPolicy() const { - // Note: this will probably need PR_SET_VMA if/when it's used on - // Android without being overridden by an allow-all policy, and - // the constant will need to be defined locally. - Arg<int> op(0); - return Switch(op) - .CASES((PR_GET_SECCOMP, // BroadcastSetThreadSandbox, etc. - PR_SET_NAME, // Thread creation - PR_SET_DUMPABLE, // Crash reporting - PR_SET_PTRACER), // Debug-mode crash handling - Allow()) - .Default(InvalidSyscall()); - } - - virtual Maybe<ResultExpr> EvaluateSocketCall(int aCall) const override { - switch (aCall) { - case SYS_RECVMSG: - case SYS_SENDMSG: - return Some(Allow()); - default: - return Nothing(); - } - } - - virtual ResultExpr EvaluateSyscall(int sysno) const override { - switch (sysno) { - // Timekeeping - case __NR_clock_gettime: { - Arg<clockid_t> clk_id(0); - return If(clk_id == CLOCK_MONOTONIC, Allow()) -#ifdef CLOCK_MONOTONIC_COARSE - .ElseIf(clk_id == CLOCK_MONOTONIC_COARSE, Allow()) -#endif - .ElseIf(clk_id == CLOCK_PROCESS_CPUTIME_ID, Allow()) - .ElseIf(clk_id == CLOCK_REALTIME, Allow()) -#ifdef CLOCK_REALTIME_COARSE - .ElseIf(clk_id == CLOCK_REALTIME_COARSE, Allow()) -#endif - .ElseIf(clk_id == CLOCK_THREAD_CPUTIME_ID, Allow()) - .Else(InvalidSyscall()); - } - case __NR_gettimeofday: -#ifdef __NR_time - case __NR_time: -#endif - case __NR_nanosleep: - return Allow(); - - // Thread synchronization - case __NR_futex: - // FIXME: This could be more restrictive.... - return Allow(); - - // Asynchronous I/O - case __NR_epoll_wait: - case __NR_epoll_pwait: - case __NR_epoll_ctl: - case __NR_ppoll: - case __NR_poll: - return Allow(); - - // Used when requesting a crash dump. - case __NR_pipe: - return Allow(); - - // Metadata of opened files - CASES_FOR_fstat: - return Allow(); - - // Simple I/O - case __NR_write: - case __NR_read: - case __NR_readv: - case __NR_writev: // see SandboxLogging.cpp - CASES_FOR_lseek: - return Allow(); - - // Memory mapping - CASES_FOR_mmap: - case __NR_munmap: - return Allow(); - - // Signal handling -#if defined(ANDROID) || defined(MOZ_ASAN) - case __NR_sigaltstack: -#endif - CASES_FOR_sigreturn: - CASES_FOR_sigprocmask: - CASES_FOR_sigaction: - return Allow(); - - // Send signals within the process (raise(), profiling, etc.) - case __NR_tgkill: { - Arg<pid_t> tgid(0); - return If(tgid == getpid(), Allow()) - .Else(InvalidSyscall()); - } - -#if defined(ANDROID) && ANDROID_VERSION < 16 - // Polyfill with tgkill; see above. - case __NR_tkill: - return Trap(TKillCompatTrap, nullptr); -#endif - - // Yield - case __NR_sched_yield: - return Allow(); - - // Thread creation. - case __NR_clone: - return ClonePolicy(InvalidSyscall()); - - // More thread creation. -#ifdef __NR_set_robust_list - case __NR_set_robust_list: - return Allow(); -#endif -#ifdef ANDROID - case __NR_set_tid_address: - return Allow(); -#endif - - // prctl - case __NR_prctl: { - if (SandboxInfo::Get().Test(SandboxInfo::kHasSeccompTSync)) { - return PrctlPolicy(); - } - - Arg<int> option(0); - return If(option == PR_SET_NO_NEW_PRIVS, - Trap(SetNoNewPrivsTrap, nullptr)) - .Else(PrctlPolicy()); - } - - // NSPR can call this when creating a thread, but it will accept a - // polite "no". - case __NR_getpriority: - // But if thread creation races with sandbox startup, that call - // could succeed, and then we get one of these: - case __NR_setpriority: - return Error(EACCES); - - // Stack bounds are obtained via pthread_getattr_np, which calls - // this but doesn't actually need it: - case __NR_sched_getaffinity: - return Error(ENOSYS); - - // Read own pid/tid. - case __NR_getpid: - case __NR_gettid: - return Allow(); - - // Discard capabilities - case __NR_close: - return Allow(); - - // Machine-dependent stuff -#ifdef __arm__ - case __ARM_NR_breakpoint: - case __ARM_NR_cacheflush: - case __ARM_NR_usr26: // FIXME: do we actually need this? - case __ARM_NR_usr32: - case __ARM_NR_set_tls: - return Allow(); -#endif - - // Needed when being debugged: - case __NR_restart_syscall: - return Allow(); - - // Terminate threads or the process - case __NR_exit: - case __NR_exit_group: - return Allow(); - -#ifdef MOZ_ASAN - // ASAN's error reporter wants to know if stderr is a tty. - case __NR_ioctl: { - Arg<int> fd(0); - return If(fd == STDERR_FILENO, Allow()) - .Else(InvalidSyscall()); - } - - // ...and before compiler-rt r209773, it will call readlink on - // /proc/self/exe and use the cached value only if that fails: - case __NR_readlink: - case __NR_readlinkat: - return Error(ENOENT); - - // ...and if it found an external symbolizer, it will try to run it: - // (See also bug 1081242 comment #7.) - CASES_FOR_stat: - return Error(ENOENT); -#endif - - default: - return SandboxPolicyBase::EvaluateSyscall(sysno); - } - } -}; - -// The process-type-specific syscall rules start here: - -} diff --git a/security/sandbox/linux/SandboxFilter.h b/security/sandbox/linux/SandboxFilter.h deleted file mode 100644 index b6031d30e..000000000 --- a/security/sandbox/linux/SandboxFilter.h +++ /dev/null @@ -1,23 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxFilter_h -#define mozilla_SandboxFilter_h - -#include "mozilla/Atomics.h" -#include "mozilla/UniquePtr.h" - -namespace sandbox { -namespace bpf_dsl { -class Policy; -} -} - -namespace mozilla { - -} // namespace mozilla - -#endif diff --git a/security/sandbox/linux/SandboxFilterUtil.cpp b/security/sandbox/linux/SandboxFilterUtil.cpp deleted file mode 100644 index 04fd6709c..000000000 --- a/security/sandbox/linux/SandboxFilterUtil.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* -*- 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 "SandboxFilterUtil.h" - -#ifndef ANDROID -#include <linux/ipc.h> -#endif -#include <linux/net.h> - -#include "mozilla/UniquePtr.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" - -// Older kernel headers (mostly Android, but also some older desktop -// distributions) are missing some or all of these: -#ifndef SYS_ACCEPT4 -#define SYS_ACCEPT4 18 -#endif -#ifndef SYS_RECVMMSG -#define SYS_RECVMMSG 19 -#endif -#ifndef SYS_SENDMMSG -#define SYS_SENDMMSG 20 -#endif - -using namespace sandbox::bpf_dsl; -#define CASES SANDBOX_BPF_DSL_CASES - -namespace mozilla { - -sandbox::bpf_dsl::ResultExpr -SandboxPolicyBase::EvaluateSyscall(int aSysno) const { - switch (aSysno) { -#ifdef __NR_socketcall - case __NR_socketcall: { - Arg<int> call(0); - UniquePtr<Caser<int>> acc(new Caser<int>(Switch(call))); - for (int i = SYS_SOCKET; i <= SYS_SENDMMSG; ++i) { - auto thisCase = EvaluateSocketCall(i); - // Optimize out cases that are equal to the default. - if (thisCase) { - acc.reset(new Caser<int>(acc->Case(i, *thisCase))); - } - } - return acc->Default(InvalidSyscall()); - } -#ifndef ANDROID - case __NR_ipc: { - Arg<int> callAndVersion(0); - auto call = callAndVersion & 0xFFFF; - UniquePtr<Caser<int>> acc(new Caser<int>(Switch(call))); - for (int i = SEMOP; i <= DIPC; ++i) { - auto thisCase = EvaluateIpcCall(i); - // Optimize out cases that are equal to the default. - if (thisCase) { - acc.reset(new Caser<int>(acc->Case(i, *thisCase))); - } - } - return acc->Default(InvalidSyscall()); - } -#endif // ANDROID -#endif // __NR_socketcall -#define DISPATCH_SOCKETCALL(sysnum, socketnum) \ - case sysnum: \ - return EvaluateSocketCall(socketnum).valueOr(InvalidSyscall()) -#ifdef __NR_socket - DISPATCH_SOCKETCALL(__NR_socket, SYS_SOCKET); - DISPATCH_SOCKETCALL(__NR_bind, SYS_BIND); - DISPATCH_SOCKETCALL(__NR_connect, SYS_CONNECT); - DISPATCH_SOCKETCALL(__NR_listen, SYS_LISTEN); -#ifdef __NR_accept - DISPATCH_SOCKETCALL(__NR_accept, SYS_ACCEPT); -#endif - DISPATCH_SOCKETCALL(__NR_getsockname, SYS_GETSOCKNAME); - DISPATCH_SOCKETCALL(__NR_getpeername, SYS_GETPEERNAME); - DISPATCH_SOCKETCALL(__NR_socketpair, SYS_SOCKETPAIR); -#ifdef __NR_send - DISPATCH_SOCKETCALL(__NR_send, SYS_SEND); - DISPATCH_SOCKETCALL(__NR_recv, SYS_RECV); -#endif // __NR_send - DISPATCH_SOCKETCALL(__NR_sendto, SYS_SENDTO); - DISPATCH_SOCKETCALL(__NR_recvfrom, SYS_RECVFROM); - DISPATCH_SOCKETCALL(__NR_shutdown, SYS_SHUTDOWN); - DISPATCH_SOCKETCALL(__NR_setsockopt, SYS_SETSOCKOPT); - DISPATCH_SOCKETCALL(__NR_getsockopt, SYS_GETSOCKOPT); - DISPATCH_SOCKETCALL(__NR_sendmsg, SYS_SENDMSG); - DISPATCH_SOCKETCALL(__NR_recvmsg, SYS_RECVMSG); - DISPATCH_SOCKETCALL(__NR_accept4, SYS_ACCEPT4); - DISPATCH_SOCKETCALL(__NR_recvmmsg, SYS_RECVMMSG); - DISPATCH_SOCKETCALL(__NR_sendmmsg, SYS_SENDMMSG); -#endif // __NR_socket -#undef DISPATCH_SOCKETCALL -#ifndef __NR_socketcall -#ifndef ANDROID -#define DISPATCH_SYSVCALL(sysnum, ipcnum) \ - case sysnum: \ - return EvaluateIpcCall(ipcnum).valueOr(InvalidSyscall()) - DISPATCH_SYSVCALL(__NR_semop, SEMOP); - DISPATCH_SYSVCALL(__NR_semget, SEMGET); - DISPATCH_SYSVCALL(__NR_semctl, SEMCTL); - DISPATCH_SYSVCALL(__NR_semtimedop, SEMTIMEDOP); - DISPATCH_SYSVCALL(__NR_msgsnd, MSGSND); - DISPATCH_SYSVCALL(__NR_msgrcv, MSGRCV); - DISPATCH_SYSVCALL(__NR_msgget, MSGGET); - DISPATCH_SYSVCALL(__NR_msgctl, MSGCTL); - DISPATCH_SYSVCALL(__NR_shmat, SHMAT); - DISPATCH_SYSVCALL(__NR_shmdt, SHMDT); - DISPATCH_SYSVCALL(__NR_shmget, SHMGET); - DISPATCH_SYSVCALL(__NR_shmctl, SHMCTL); -#undef DISPATCH_SYSVCALL -#endif // ANDROID -#endif // __NR_socketcall - default: - return InvalidSyscall(); - } -} - -} diff --git a/security/sandbox/linux/SandboxFilterUtil.h b/security/sandbox/linux/SandboxFilterUtil.h deleted file mode 100644 index fb9afa79f..000000000 --- a/security/sandbox/linux/SandboxFilterUtil.h +++ /dev/null @@ -1,148 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxFilterUtil_h -#define mozilla_SandboxFilterUtil_h - -// This header file exists to hold helper code for SandboxFilter.cpp, -// to make that file easier to read for anyone trying to understand -// the filter policy. It's mostly about smoothing out differences -// between different Linux architectures. - -#include "mozilla/Maybe.h" -#include "sandbox/linux/bpf_dsl/policy.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" - -namespace mozilla { - -// This class handles syscalls for BSD socket and SysV IPC operations. -// On 32-bit x86 they're multiplexed via socketcall(2) and ipc(2), -// respectively; on most other architectures they're individual system -// calls. It translates the syscalls into socketcall/ipc selector -// values, because those are defined (even if not used) for all -// architectures. -// -// This EvaluateSyscall() routine always returns InvalidSyscall() for -// everything else. It's assumed that subclasses will be implementing -// a whitelist policy, so they can handle what they're whitelisting -// and then defer to this class in the default case. -class SandboxPolicyBase : public sandbox::bpf_dsl::Policy -{ -public: - using ResultExpr = sandbox::bpf_dsl::ResultExpr; - - virtual ResultExpr EvaluateSyscall(int aSysno) const override; - virtual Maybe<ResultExpr> EvaluateSocketCall(int aCall) const { - return Nothing(); - } -#ifndef ANDROID - // Android doesn't use SysV IPC (and doesn't define the selector - // constants in its headers), so this isn't implemented there. - virtual Maybe<ResultExpr> EvaluateIpcCall(int aCall) const { - return Nothing(); - } -#endif - -#ifdef __NR_socketcall - // socketcall(2) takes the actual call's arguments via a pointer, so - // seccomp-bpf can't inspect them; ipc(2) takes them at different indices. - static const bool kSocketCallHasArgs = false; - static const bool kIpcCallNormalArgs = false; -#else - // Otherwise, the bpf_dsl Arg<> class can be used normally. - static const bool kSocketCallHasArgs = true; - static const bool kIpcCallNormalArgs = true; -#endif -}; - -} // namespace mozilla - -// "Machine independent" pseudo-syscall numbers, to deal with arch -// dependencies. (Most 32-bit archs started with 32-bit off_t; older -// archs started with 16-bit uid_t/gid_t; 32-bit registers can't hold -// a 64-bit offset for mmap; and so on.) -// -// For some of these, the "old" syscalls are also in use in some -// cases; see, e.g., the handling of RT vs. non-RT signal syscalls. - -#ifdef __NR_mmap2 -#define CASES_FOR_mmap case __NR_mmap2 -#else -#define CASES_FOR_mmap case __NR_mmap -#endif - -#ifdef __NR_fchown32 -#define CASES_FOR_fchown case __NR_fchown32: case __NR_fchown -#else -#define CASES_FOR_fchown case __NR_fchown -#endif - -#ifdef __NR_getuid32 -#define CASES_FOR_getuid case __NR_getuid32 -#define CASES_FOR_getgid case __NR_getgid32 -#define CASES_FOR_geteuid case __NR_geteuid32 -#define CASES_FOR_getegid case __NR_getegid32 -#define CASES_FOR_getresuid case __NR_getresuid32: case __NR_getresuid -#define CASES_FOR_getresgid case __NR_getresgid32: case __NR_getresgid -// The set*id syscalls are omitted; we'll probably never need to allow them. -#else -#define CASES_FOR_getuid case __NR_getuid -#define CASES_FOR_getgid case __NR_getgid -#define CASES_FOR_geteuid case __NR_geteuid -#define CASES_FOR_getegid case __NR_getegid -#define CASES_FOR_getresuid case __NR_getresuid -#define CASES_FOR_getresgid case __NR_getresgid -#endif - -#ifdef __NR_stat64 -#define CASES_FOR_stat case __NR_stat64 -#define CASES_FOR_lstat case __NR_lstat64 -#define CASES_FOR_fstat case __NR_fstat64 -#define CASES_FOR_fstatat case __NR_fstatat64 -#define CASES_FOR_statfs case __NR_statfs64: case __NR_statfs -#define CASES_FOR_fstatfs case __NR_fstatfs64: case __NR_fstatfs -#define CASES_FOR_fcntl case __NR_fcntl64 -// We're using the 32-bit version on 32-bit desktop for some reason. -#define CASES_FOR_getdents case __NR_getdents64: case __NR_getdents -// FIXME: we might not need the compat cases for these on non-Android: -#define CASES_FOR_lseek case __NR_lseek: case __NR__llseek -#define CASES_FOR_ftruncate case __NR_ftruncate: case __NR_ftruncate64 -#else -#define CASES_FOR_stat case __NR_stat -#define CASES_FOR_lstat case __NR_lstat -#define CASES_FOR_fstatat case __NR_newfstatat -#define CASES_FOR_fstat case __NR_fstat -#define CASES_FOR_fstatfs case __NR_fstatfs -#define CASES_FOR_statfs case __NR_statfs -#define CASES_FOR_fcntl case __NR_fcntl -#define CASES_FOR_getdents case __NR_getdents -#define CASES_FOR_lseek case __NR_lseek -#define CASES_FOR_ftruncate case __NR_ftruncate -#endif - -#ifdef __NR_sigprocmask -#define CASES_FOR_sigprocmask case __NR_sigprocmask: case __NR_rt_sigprocmask -#define CASES_FOR_sigaction case __NR_sigaction: case __NR_rt_sigaction -#define CASES_FOR_sigreturn case __NR_sigreturn: case __NR_rt_sigreturn -#else -#define CASES_FOR_sigprocmask case __NR_rt_sigprocmask -#define CASES_FOR_sigaction case __NR_rt_sigaction -#define CASES_FOR_sigreturn case __NR_rt_sigreturn -#endif - -#ifdef __NR__newselect -#define CASES_FOR_select case __NR__newselect -#else -#define CASES_FOR_select case __NR_select -#endif - -#ifdef __NR_ugetrlimit -#define CASES_FOR_getrlimit case __NR_ugetrlimit -#else -#define CASES_FOR_getrlimit case __NR_getrlimit -#endif - -#endif // mozilla_SandboxFilterUtil_h diff --git a/security/sandbox/linux/SandboxHooks.cpp b/security/sandbox/linux/SandboxHooks.cpp deleted file mode 100644 index eaaf56982..000000000 --- a/security/sandbox/linux/SandboxHooks.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- 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 <dlfcn.h> -#include <signal.h> -#include <errno.h> - -#include "mozilla/Types.h" - -#include <stdio.h> -#include <stdlib.h> - -// Signal number used to enable seccomp on each thread. -extern int gSeccompTsyncBroadcastSignum; - -// This file defines a hook for sigprocmask() and pthread_sigmask(). -// Bug 1176099: some threads block SIGSYS signal which breaks our seccomp-bpf -// sandbox. To avoid this, we intercept the call and remove SIGSYS. -// -// ENOSYS indicates an error within the hook function itself. -static int HandleSigset(int (*aRealFunc)(int, const sigset_t*, sigset_t*), - int aHow, const sigset_t* aSet, - sigset_t* aOldSet, bool aUseErrno) -{ - if (!aRealFunc) { - if (aUseErrno) { - errno = ENOSYS; - return -1; - } - - return ENOSYS; - } - - // Avoid unnecessary work - if (aSet == NULL || aHow == SIG_UNBLOCK) { - return aRealFunc(aHow, aSet, aOldSet); - } - - sigset_t newSet = *aSet; - if (sigdelset(&newSet, SIGSYS) != 0 || - (gSeccompTsyncBroadcastSignum && - sigdelset(&newSet, gSeccompTsyncBroadcastSignum) != 0)) { - if (aUseErrno) { - errno = ENOSYS; - return -1; - } - - return ENOSYS; - } - - return aRealFunc(aHow, &newSet, aOldSet); -} - -extern "C" MOZ_EXPORT int -sigprocmask(int how, const sigset_t* set, sigset_t* oldset) -{ - static auto sRealFunc = (int (*)(int, const sigset_t*, sigset_t*)) - dlsym(RTLD_NEXT, "sigprocmask"); - - return HandleSigset(sRealFunc, how, set, oldset, true); -} - -extern "C" MOZ_EXPORT int -pthread_sigmask(int how, const sigset_t* set, sigset_t* oldset) -{ - static auto sRealFunc = (int (*)(int, const sigset_t*, sigset_t*)) - dlsym(RTLD_NEXT, "pthread_sigmask"); - - return HandleSigset(sRealFunc, how, set, oldset, false); -} diff --git a/security/sandbox/linux/SandboxInfo.cpp b/security/sandbox/linux/SandboxInfo.cpp deleted file mode 100644 index 2eb65e39c..000000000 --- a/security/sandbox/linux/SandboxInfo.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* -*- 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 "SandboxInfo.h" -#include "SandboxLogging.h" -#include "LinuxSched.h" - -#include <errno.h> -#include <stdlib.h> -#include <sys/prctl.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "base/posix/eintr_wrapper.h" -#include "mozilla/Assertions.h" -#include "mozilla/ArrayUtils.h" -#include "sandbox/linux/system_headers/linux_seccomp.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" - -#ifdef MOZ_VALGRIND -#include <valgrind/valgrind.h> -#endif - - -// A note about assertions: in general, the worst thing this module -// should be able to do is disable sandboxing features, so release -// asserts or MOZ_CRASH should be avoided, even for seeming -// impossibilities like an unimplemented syscall returning success -// (which has happened: https://crbug.com/439795 ). -// -// MOZ_DIAGNOSTIC_ASSERT (debug builds, plus Nightly/Aurora non-debug) -// is probably the best choice for conditions that shouldn't be able -// to fail without the help of bugs in the kernel or system libraries. -// -// Regardless of assertion type, whatever condition caused it to fail -// should generally also disable the corresponding feature on builds -// that omit the assertion. - -namespace mozilla { - -// Bug 1229136: this is copied from ../SandboxUtil.cpp to avoid -// complicated build issues; renamespaced to avoid the possibility of -// symbol conflict. -namespace { - -static bool -IsSingleThreaded() -{ - // This detects the thread count indirectly. /proc/<pid>/task has a - // subdirectory for each thread in <pid>'s thread group, and the - // link count on the "task" directory follows Unix expectations: the - // link from its parent, the "." link from itself, and the ".." link - // from each subdirectory; thus, 2+N links for N threads. - struct stat sb; - if (stat("/proc/self/task", &sb) < 0) { - MOZ_DIAGNOSTIC_ASSERT(false, "Couldn't access /proc/self/task!"); - return false; - } - MOZ_DIAGNOSTIC_ASSERT(sb.st_nlink >= 3); - return sb.st_nlink == 3; -} - -} // anonymous namespace - -static bool -HasSeccompBPF() -{ - // Allow simulating the absence of seccomp-bpf support, for testing. - if (getenv("MOZ_FAKE_NO_SANDBOX")) { - return false; - } - - // Valgrind and the sandbox don't interact well, probably because Valgrind - // does various system calls which aren't allowed, even if Firefox itself - // is playing by the rules. -# if defined(MOZ_VALGRIND) - if (RUNNING_ON_VALGRIND) { - return false; - } -# endif - - // Determine whether seccomp-bpf is supported by trying to - // enable it with an invalid pointer for the filter. This will - // fail with EFAULT if supported and EINVAL if not, without - // changing the process's state. - - int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr); - MOZ_DIAGNOSTIC_ASSERT(rv == -1, "prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER," - " nullptr) didn't fail"); - MOZ_DIAGNOSTIC_ASSERT(errno == EFAULT || errno == EINVAL); - return rv == -1 && errno == EFAULT; -} - -static bool -HasSeccompTSync() -{ - // Similar to above, but for thread-sync mode. See also Chromium's - // sandbox::SandboxBPF::SupportsSeccompThreadFilterSynchronization - if (getenv("MOZ_FAKE_NO_SECCOMP_TSYNC")) { - return false; - } - int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, - SECCOMP_FILTER_FLAG_TSYNC, nullptr); - MOZ_DIAGNOSTIC_ASSERT(rv == -1, "seccomp(..., SECCOMP_FILTER_FLAG_TSYNC," - " nullptr) didn't fail"); - MOZ_DIAGNOSTIC_ASSERT(errno == EFAULT || errno == EINVAL || errno == ENOSYS); - return rv == -1 && errno == EFAULT; -} - -static bool -HasUserNamespaceSupport() -{ - // Note: the /proc/<pid>/ns/* files track setns(2) support, which in - // some cases (e.g., pid) significantly postdates kernel support for - // the namespace type, so in general this type of check could be a - // false negative. However, for user namespaces, any kernel new - // enough for the feature to be usable for us has setns support - // (v3.8), so this is okay. - // - // The non-user namespaces all default to "y" in init/Kconfig, but - // check them explicitly in case someone has a weird custom config. - static const char* const paths[] = { - "/proc/self/ns/user", - "/proc/self/ns/pid", - "/proc/self/ns/net", - "/proc/self/ns/ipc", - }; - for (size_t i = 0; i < ArrayLength(paths); ++i) { - if (access(paths[i], F_OK) == -1) { - MOZ_ASSERT(errno == ENOENT); - return false; - } - } - return true; -} - -static bool -CanCreateUserNamespace() -{ - // Unfortunately, the only way to verify that this process can - // create a new user namespace is to actually create one; because - // this process's namespaces shouldn't be side-effected (yet), it's - // necessary to clone (and collect) a child process. See also - // Chromium's sandbox::Credentials::SupportsNewUserNS. - // - // This is somewhat more expensive than the other tests, so it's - // cached in the environment to prevent child processes from having - // to re-run the test. - // - // This is run at static initializer time, while single-threaded, so - // locking isn't needed to access the environment. - static const char kCacheEnvName[] = "MOZ_ASSUME_USER_NS"; - const char* cached = getenv(kCacheEnvName); - if (cached) { - return cached[0] > '0'; - } - - // Valgrind might allow the clone, but doesn't know what to do with - // unshare. Check for that by unsharing nothing. (Valgrind will - // probably need sandboxing disabled entirely, but no need to break - // things worse than strictly necessary.) - if (syscall(__NR_unshare, 0) != 0) { -#ifdef MOZ_VALGRIND - MOZ_ASSERT(errno == ENOSYS); -#else - // If something else can cause that call to fail, we's like to know - // about it; the right way to handle it might not be the same. - MOZ_ASSERT(false); -#endif - return false; - } - - pid_t pid = syscall(__NR_clone, SIGCHLD | CLONE_NEWUSER, - nullptr, nullptr, nullptr, nullptr); - if (pid == 0) { - // In the child. Do as little as possible. - _exit(0); - } - if (pid == -1) { - // Failure. - MOZ_ASSERT(errno == EINVAL || // unsupported - errno == EPERM || // root-only, or we're already chrooted - errno == EUSERS); // already at user namespace nesting limit - setenv(kCacheEnvName, "0", 1); - return false; - } - // Otherwise, in the parent and successful. - bool waitpid_ok = HANDLE_EINTR(waitpid(pid, nullptr, 0)) == pid; - MOZ_ASSERT(waitpid_ok); - if (!waitpid_ok) { - return false; - } - setenv(kCacheEnvName, "1", 1); - return true; -} - -/* static */ -SandboxInfo SandboxInfo::sSingleton = SandboxInfo(); - -SandboxInfo::SandboxInfo() { - int flags = 0; - static_assert(sizeof(flags) >= sizeof(Flags), "enum Flags fits in an int"); - - if (HasSeccompBPF()) { - flags |= kHasSeccompBPF; - if (HasSeccompTSync()) { - flags |= kHasSeccompTSync; - } - } - - // Detect the threading-problem signal from the parent process. - if (getenv("MOZ_SANDBOX_UNEXPECTED_THREADS")) { - flags |= kUnexpectedThreads; - } else { - if (HasUserNamespaceSupport()) { - flags |= kHasPrivilegedUserNamespaces; - if (CanCreateUserNamespace()) { - flags |= kHasUserNamespaces; - } - } - } - - if (getenv("MOZ_SANDBOX_VERBOSE")) { - flags |= kVerbose; - } - - mFlags = static_cast<Flags>(flags); -} - -/* static */ void -SandboxInfo::ThreadingCheck() -{ - // Allow MOZ_SANDBOX_UNEXPECTED_THREADS to be set manually for testing. - if (IsSingleThreaded() && - !getenv("MOZ_SANDBOX_UNEXPECTED_THREADS")) { - return; - } - SANDBOX_LOG_ERROR("unexpected multithreading found; this prevents using" - " namespace sandboxing.%s", - // getenv isn't thread-safe, but see below. - getenv("LD_PRELOAD") ? " (If you're LD_PRELOAD'ing" - " nVidia GL: that's not necessary for Gecko.)" : ""); - - // Propagate this information for use by child processes. (setenv - // isn't thread-safe, but other threads are from non-Gecko code so - // they wouldn't be using NSPR; we have to hope for the best.) - setenv("MOZ_SANDBOX_UNEXPECTED_THREADS", "1", 0); - int flags = sSingleton.mFlags; - flags |= kUnexpectedThreads; - flags &= ~(kHasUserNamespaces | kHasPrivilegedUserNamespaces); - sSingleton.mFlags = static_cast<Flags>(flags); -} - -} // namespace mozilla diff --git a/security/sandbox/linux/SandboxInfo.h b/security/sandbox/linux/SandboxInfo.h deleted file mode 100644 index 1999ac392..000000000 --- a/security/sandbox/linux/SandboxInfo.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxInfo_h -#define mozilla_SandboxInfo_h - -#include "mozilla/Types.h" - -// Information on what parts of sandboxing are enabled in this build -// and/or supported by the system. - -namespace mozilla { - -class SandboxInfo { -public: - // No need to prevent copying; this is essentially just a const int. - SandboxInfo(const SandboxInfo& aOther) : mFlags(aOther.mFlags) { } - - // Flags are checked at initializer time; this returns them. - static const SandboxInfo& Get() { return sSingleton; } - - enum Flags { - // System call filtering; kernel config option CONFIG_SECCOMP_FILTER. - kHasSeccompBPF = 1 << 0, - // Config flag MOZ_CONTENT_SANDBOX; env var MOZ_DISABLE_CONTENT_SANDBOX. - kEnabledForContent = 1 << 1, - // Config flag MOZ_GMP_SANDBOX; env var MOZ_DISABLE_GMP_SANDBOX. - kEnabledForMedia = 1 << 2, - // Env var MOZ_SANDBOX_VERBOSE. - kVerbose = 1 << 3, - // Kernel can atomically set system call filtering on entire thread group. - kHasSeccompTSync = 1 << 4, - // Can this process create user namespaces? (Man page user_namespaces(7).) - kHasUserNamespaces = 1 << 5, - // Could a more privileged process have user namespaces, even if we can't? - kHasPrivilegedUserNamespaces = 1 << 6, - // Env var MOZ_PERMISSIVE_CONTENT_SANDBOX - kPermissive = 1 << 7, - // Something is creating threads when we need to still be single-threaded. - kUnexpectedThreads = 1 << 8, - }; - - bool Test(Flags aFlag) const { return (mFlags & aFlag) == aFlag; } - - // Returns true if SetContentProcessSandbox may be called. - bool CanSandboxContent() const - { - return !Test(kEnabledForContent) || Test(kHasSeccompBPF); - } - - // Returns true if SetMediaPluginSandbox may be called. - bool CanSandboxMedia() const - { - return !Test(kEnabledForMedia) || Test(kHasSeccompBPF); - } - - // For telemetry / crash annotation uses. - uint32_t AsInteger() const { - return mFlags; - } - - // For bug 1222500 or anything else like it: On desktop, this is - // called in the parent process at a point when it should still be - // single-threaded, to check that the SandboxEarlyInit() call in a - // child process is early enough to be single-threaded. If not, - // kUnexpectedThreads is set and affected flags (user namespaces; - // possibly others in the future) are cleared. - static MOZ_EXPORT void ThreadingCheck(); -private: - enum Flags mFlags; - // This should be const, but has to allow for ThreadingCheck. - static MOZ_EXPORT SandboxInfo sSingleton; - SandboxInfo(); -}; - -} // namespace mozilla - -#endif // mozilla_SandboxInfo_h diff --git a/security/sandbox/linux/SandboxInternal.h b/security/sandbox/linux/SandboxInternal.h deleted file mode 100644 index 90a688421..000000000 --- a/security/sandbox/linux/SandboxInternal.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxInternal_h -#define mozilla_SandboxInternal_h - -#include <signal.h> - -#include "mozilla/Types.h" - -struct sock_fprog; - -namespace mozilla { - -// SandboxCrash() has to be in libxul to use internal interfaces, but -// its caller in libmozsandbox. -// See also bug 1101170. - -typedef void (*SandboxCrashFunc)(int, siginfo_t*, void*); -extern MOZ_EXPORT SandboxCrashFunc gSandboxCrashFunc; -extern const sock_fprog* gSetSandboxFilter; - -} // namespace mozilla - -#endif // mozilla_SandboxInternal_h diff --git a/security/sandbox/linux/SandboxLogging.cpp b/security/sandbox/linux/SandboxLogging.cpp deleted file mode 100644 index 19196a75a..000000000 --- a/security/sandbox/linux/SandboxLogging.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* -*- 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 "SandboxLogging.h" - -#ifdef ANDROID -#include <android/log.h> -#endif -#include <algorithm> -#include <stdio.h> -#include <string.h> -#include <sys/uio.h> -#include <unistd.h> - -#include "base/posix/eintr_wrapper.h" - -namespace mozilla { - -// Alters an iovec array to remove the first `toDrop` bytes. This -// complexity is necessary because writev can return a short write -// (e.g., if stderr is a pipe and the buffer is almost full). -static void -IOVecDrop(struct iovec* iov, int iovcnt, size_t toDrop) -{ - while (toDrop > 0 && iovcnt > 0) { - size_t toDropHere = std::min(toDrop, iov->iov_len); - iov->iov_base = static_cast<char*>(iov->iov_base) + toDropHere; - iov->iov_len -= toDropHere; - toDrop -= toDropHere; - ++iov; - --iovcnt; - } -} - -void -SandboxLogError(const char* message) -{ -#ifdef ANDROID - // This uses writev internally and appears to be async signal safe. - __android_log_write(ANDROID_LOG_ERROR, "Sandbox", message); -#endif - static const char logPrefix[] = "Sandbox: ", logSuffix[] = "\n"; - struct iovec iovs[3] = { - { const_cast<char*>(logPrefix), sizeof(logPrefix) - 1 }, - { const_cast<char*>(message), strlen(message) }, - { const_cast<char*>(logSuffix), sizeof(logSuffix) - 1 }, - }; - while (iovs[2].iov_len > 0) { - ssize_t written = HANDLE_EINTR(writev(STDERR_FILENO, iovs, 3)); - if (written <= 0) { - break; - } - IOVecDrop(iovs, 3, static_cast<size_t>(written)); - } -} - -} diff --git a/security/sandbox/linux/SandboxLogging.h b/security/sandbox/linux/SandboxLogging.h deleted file mode 100644 index 88891adfb..000000000 --- a/security/sandbox/linux/SandboxLogging.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxLogging_h -#define mozilla_SandboxLogging_h - -// This header defines the SANDBOX_LOG_ERROR macro used in the Linux -// sandboxing code. It uses Android logging on Android and writes to -// stderr otherwise. Android logging has severity levels; currently -// only "error" severity is exposed here, and this isn't marked when -// writing to stderr. -// -// The format strings are processed by Chromium SafeSPrintf, which -// doesn't accept size modifiers or %u because it uses C++11 variadic -// templates to obtain the actual argument types; all decimal integer -// formatting uses %d. See safe_sprintf.h for more details. - -// Build SafeSPrintf without assertions to avoid a dependency on -// Chromium logging. This doesn't affect safety; it just means that -// type mismatches (pointer vs. integer) always result in unexpanded -// %-directives instead of crashing. See also the moz.build files, -// which apply NDEBUG to the .cc file. -#ifndef NDEBUG -#define NDEBUG 1 -#include "base/strings/safe_sprintf.h" -#undef NDEBUG -#else -#include "base/strings/safe_sprintf.h" -#endif - -namespace mozilla { -// Logs the formatted string (marked with "error" severity, if supported). -void SandboxLogError(const char* aMessage); -} - -#define SANDBOX_LOG_LEN 256 - -// Formats a log message and logs it (with "error" severity, if supported). -// -// Note that SafeSPrintf doesn't accept size modifiers or %u; all -// decimal integers are %d, because it uses C++11 variadic templates -// to use the actual argument type. -#define SANDBOX_LOG_ERROR(fmt, args...) do { \ - char _sandboxLogBuf[SANDBOX_LOG_LEN]; \ - ::base::strings::SafeSPrintf(_sandboxLogBuf, fmt, ## args); \ - ::mozilla::SandboxLogError(_sandboxLogBuf); \ -} while(0) - -#endif // mozilla_SandboxLogging_h diff --git a/security/sandbox/linux/SandboxUtil.cpp b/security/sandbox/linux/SandboxUtil.cpp deleted file mode 100644 index 999329882..000000000 --- a/security/sandbox/linux/SandboxUtil.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- 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 "SandboxUtil.h" - -#include "LinuxCapabilities.h" -#include "LinuxSched.h" -#include "SandboxLogging.h" - -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include "mozilla/Assertions.h" -#include "mozilla/Sprintf.h" -#include "mozilla/Unused.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" - -namespace mozilla { - -bool -IsSingleThreaded() -{ - // This detects the thread count indirectly. /proc/<pid>/task has a - // subdirectory for each thread in <pid>'s thread group, and the - // link count on the "task" directory follows Unix expectations: the - // link from its parent, the "." link from itself, and the ".." link - // from each subdirectory; thus, 2+N links for N threads. - struct stat sb; - if (stat("/proc/self/task", &sb) < 0) { - MOZ_DIAGNOSTIC_ASSERT(false, "Couldn't access /proc/self/task!"); - return false; - } - MOZ_DIAGNOSTIC_ASSERT(sb.st_nlink >= 3); - return sb.st_nlink == 3; -} - -static bool -WriteStringToFile(const char* aPath, const char* aStr, const size_t aLen) -{ - int fd = open(aPath, O_WRONLY); - if (fd < 0) { - return false; - } - ssize_t written = write(fd, aStr, aLen); - if (close(fd) != 0 || written != ssize_t(aLen)) { - return false; - } - return true; -} - -bool -UnshareUserNamespace() -{ - // The uid and gid need to be retrieved before the unshare; see - // below. - uid_t uid = getuid(); - gid_t gid = getgid(); - char buf[80]; - - if (syscall(__NR_unshare, CLONE_NEWUSER) != 0) { - return false; - } - - // As mentioned in the header, this function sets up uid/gid - // mappings that preserve the process's previous ids. Mapping the - // uid/gid to something is necessary in order to nest user - // namespaces (not used yet, but we'll need this in the future for - // pid namespace support), and leaving the ids unchanged is the - // least confusing option. - // - // In recent kernels (3.19, 3.18.2, 3.17.8), for security reasons, - // establishing gid mappings will fail unless the process first - // revokes its ability to call setgroups() by using a /proc node - // added in the same set of patches. - // - // Note that /proc/self points to the thread group leader, not the - // current thread. However, CLONE_NEWUSER can be unshared only in a - // single-threaded process, so those are equivalent if we reach this - // point. - int len = SprintfLiteral(buf, "%u %u 1\n", uid, uid); - if (len >= int(sizeof(buf)) || len < 0) { - return false; - } - if (!WriteStringToFile("/proc/self/uid_map", buf, size_t(len))) { - MOZ_CRASH("Failed to write /proc/self/uid_map"); - } - - Unused << WriteStringToFile("/proc/self/setgroups", "deny", 4); - - len = SprintfLiteral(buf, "%u %u 1\n", gid, gid); - if (len >= int(sizeof(buf)) || len < 0) { - return false; - } - if (!WriteStringToFile("/proc/self/gid_map", buf, size_t(len))) { - MOZ_CRASH("Failed to write /proc/self/gid_map"); - } - return true; -} - -} // namespace mozilla diff --git a/security/sandbox/linux/SandboxUtil.h b/security/sandbox/linux/SandboxUtil.h deleted file mode 100644 index 7bd84f798..000000000 --- a/security/sandbox/linux/SandboxUtil.h +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxUtil_h -#define mozilla_SandboxUtil_h - -namespace mozilla { - -bool IsSingleThreaded(); - -// Unshare the user namespace, and set up id mappings so that the -// process's subjective uid and gid are unchanged. This will always -// fail if the process is multithreaded. -bool UnshareUserNamespace(); - -} // namespace mozilla - -#endif // mozilla_SandboxUtil_h diff --git a/security/sandbox/linux/broker/SandboxBroker.cpp b/security/sandbox/linux/broker/SandboxBroker.cpp deleted file mode 100644 index a31d1fc66..000000000 --- a/security/sandbox/linux/broker/SandboxBroker.cpp +++ /dev/null @@ -1,731 +0,0 @@ -/* -*- 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 "SandboxBroker.h" -#include "SandboxInfo.h" -#include "SandboxLogging.h" -#include "SandboxBrokerUtils.h" - -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#ifdef XP_LINUX -#include <sys/prctl.h> -#endif - -#ifdef MOZ_WIDGET_GONK -#include <private/android_filesystem_config.h> -#include <sys/syscall.h> -#endif - -#include "mozilla/Assertions.h" -#include "mozilla/DebugOnly.h" -#include "mozilla/Move.h" -#include "mozilla/NullPtr.h" -#include "mozilla/Sprintf.h" -#include "mozilla/ipc/FileDescriptor.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" - -namespace mozilla { - -// This constructor signals failure by setting mFileDesc and aClientFd to -1. -SandboxBroker::SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid, - int& aClientFd) - : mChildPid(aChildPid), mPolicy(Move(aPolicy)) -{ - int fds[2]; - if (0 != socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) { - SANDBOX_LOG_ERROR("SandboxBroker: socketpair failed: %s", strerror(errno)); - mFileDesc = -1; - aClientFd = -1; - return; - } - mFileDesc = fds[0]; - aClientFd = fds[1]; - - if (!PlatformThread::Create(0, this, &mThread)) { - SANDBOX_LOG_ERROR("SandboxBroker: thread creation failed: %s", - strerror(errno)); - close(mFileDesc); - close(aClientFd); - mFileDesc = -1; - aClientFd = -1; - } -} - -UniquePtr<SandboxBroker> -SandboxBroker::Create(UniquePtr<const Policy> aPolicy, int aChildPid, - ipc::FileDescriptor& aClientFdOut) -{ - int clientFd; - // Can't use MakeUnique here because the constructor is private. - UniquePtr<SandboxBroker> rv(new SandboxBroker(Move(aPolicy), aChildPid, - clientFd)); - if (clientFd < 0) { - rv = nullptr; - } else { - aClientFdOut = ipc::FileDescriptor(clientFd); - } - return Move(rv); -} - -SandboxBroker::~SandboxBroker() { - // If the constructor failed, there's nothing to be done here. - if (mFileDesc < 0) { - return; - } - - shutdown(mFileDesc, SHUT_RD); - // The thread will now get EOF even if the client hasn't exited. - PlatformThread::Join(mThread); - // Now that the thread has exited, the fd will no longer be accessed. - close(mFileDesc); - // Having ensured that this object outlives the thread, this - // destructor can now return. -} - -SandboxBroker::Policy::Policy() { } -SandboxBroker::Policy::~Policy() { } - -SandboxBroker::Policy::Policy(const Policy& aOther) { - for (auto iter = aOther.mMap.ConstIter(); !iter.Done(); iter.Next()) { - mMap.Put(iter.Key(), iter.Data()); - } -} - -// Chromium -// sandbox/linux/syscall_broker/broker_file_permission.cc -// Async signal safe -bool -SandboxBroker::Policy::ValidatePath(const char* path) const { - if (!path) - return false; - - const size_t len = strlen(path); - // No empty paths - if (len == 0) - return false; - // Paths must be absolute and not relative - if (path[0] != '/') - return false; - // No trailing / (but "/" is valid) - if (len > 1 && path[len - 1] == '/') - return false; - // No trailing /. - if (len >= 2 && path[len - 2] == '/' && path[len - 1] == '.') - return false; - // No trailing /.. - if (len >= 3 && path[len - 3] == '/' && path[len - 2] == '.' && - path[len - 1] == '.') - return false; - // No /../ anywhere - for (size_t i = 0; i < len; i++) { - if (path[i] == '/' && (len - i) > 3) { - if (path[i + 1] == '.' && path[i + 2] == '.' && path[i + 3] == '/') { - return false; - } - } - } - return true; -} - -void -SandboxBroker::Policy::AddPath(int aPerms, const char* aPath, - AddCondition aCond) -{ - nsDependentCString path(aPath); - MOZ_ASSERT(path.Length() <= kMaxPathLen); - int perms; - if (aCond == AddIfExistsNow) { - struct stat statBuf; - if (lstat(aPath, &statBuf) != 0) { - return; - } - } - if (!mMap.Get(path, &perms)) { - perms = MAY_ACCESS; - } else { - MOZ_ASSERT(perms & MAY_ACCESS); - } - if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { - SANDBOX_LOG_ERROR("policy for %s: %d -> %d", aPath, perms, perms | aPerms); - } - perms |= aPerms; - mMap.Put(path, perms); -} - -void -SandboxBroker::Policy::AddTree(int aPerms, const char* aPath) -{ - struct stat statBuf; - - if (stat(aPath, &statBuf) != 0) { - return; - } - if (!S_ISDIR(statBuf.st_mode)) { - AddPath(aPerms, aPath, AddAlways); - } else { - DIR* dirp = opendir(aPath); - if (!dirp) { - return; - } - while (struct dirent* de = readdir(dirp)) { - if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) { - continue; - } - // Note: could optimize the string handling. - nsAutoCString subPath; - subPath.Assign(aPath); - subPath.Append('/'); - subPath.Append(de->d_name); - AddTree(aPerms, subPath.get()); - } - closedir(dirp); - } -} - -void -SandboxBroker::Policy::AddDir(int aPerms, const char* aPath) -{ - struct stat statBuf; - - if (stat(aPath, &statBuf) != 0) { - return; - } - - if (!S_ISDIR(statBuf.st_mode)) { - return; - } - - nsDependentCString path(aPath); - MOZ_ASSERT(path.Length() <= kMaxPathLen - 1); - // Enforce trailing / on aPath - if (path[path.Length() - 1] != '/') { - path.Append('/'); - } - int origPerms; - if (!mMap.Get(path, &origPerms)) { - origPerms = MAY_ACCESS; - } else { - MOZ_ASSERT(origPerms & MAY_ACCESS); - } - int newPerms = origPerms | aPerms | RECURSIVE; - if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { - SANDBOX_LOG_ERROR("policy for %s: %d -> %d", aPath, origPerms, newPerms); - } - mMap.Put(path, newPerms); -} - -void -SandboxBroker::Policy::AddPrefix(int aPerms, const char* aDir, - const char* aPrefix) -{ - size_t prefixLen = strlen(aPrefix); - DIR* dirp = opendir(aDir); - struct dirent* de; - if (!dirp) { - return; - } - while ((de = readdir(dirp))) { - if (strncmp(de->d_name, aPrefix, prefixLen) == 0) { - nsAutoCString subPath; - subPath.Assign(aDir); - subPath.Append('/'); - subPath.Append(de->d_name); - AddPath(aPerms, subPath.get(), AddAlways); - } - } - closedir(dirp); -} - -int -SandboxBroker::Policy::Lookup(const nsACString& aPath) const -{ - // Early exit for paths explicitly found in the - // whitelist. - // This means they will not gain extra permissions - // from recursive paths. - int perms = mMap.Get(aPath); - if (perms) { - return perms; - } - - // Not a legally constructed path - if (!ValidatePath(PromiseFlatCString(aPath).get())) - return 0; - - // Now it's either an illegal access, or a recursive - // directory permission. We'll have to check the entire - // whitelist for the best match (slower). - int allPerms = 0; - for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) { - const nsACString& whiteListPath = iter.Key(); - const int& perms = iter.Data(); - - if (!(perms & RECURSIVE)) - continue; - - // passed part starts with something on the whitelist - if (StringBeginsWith(aPath, whiteListPath)) { - allPerms |= perms; - } - } - - // Strip away the RECURSIVE flag as it doesn't - // necessarily apply to aPath. - return allPerms & ~RECURSIVE; -} - -static bool -AllowOperation(int aReqFlags, int aPerms) -{ - int needed = 0; - if (aReqFlags & R_OK) { - needed |= SandboxBroker::MAY_READ; - } - if (aReqFlags & W_OK) { - needed |= SandboxBroker::MAY_WRITE; - } - // We don't really allow executing anything, - // so in true unix tradition we hijack this - // for directories. - if (aReqFlags & X_OK) { - needed |= SandboxBroker::MAY_CREATE; - } - return (aPerms & needed) == needed; -} - -static bool -AllowAccess(int aReqFlags, int aPerms) -{ - if (aReqFlags & ~(R_OK|W_OK|F_OK)) { - return false; - } - int needed = 0; - if (aReqFlags & R_OK) { - needed |= SandboxBroker::MAY_READ; - } - if (aReqFlags & W_OK) { - needed |= SandboxBroker::MAY_WRITE; - } - return (aPerms & needed) == needed; -} - -// These flags are added to all opens to prevent possible side-effects -// on this process. These shouldn't be relevant to the child process -// in any case due to the sandboxing restrictions on it. (See also -// the use of MSG_CMSG_CLOEXEC in SandboxBrokerCommon.cpp). -static const int kRequiredOpenFlags = O_CLOEXEC | O_NOCTTY; - -// Linux originally assigned a flag bit to O_SYNC but implemented the -// semantics standardized as O_DSYNC; later, that bit was renamed and -// a new bit was assigned to the full O_SYNC, and O_SYNC was redefined -// to be both bits. As a result, this #define is needed to compensate -// for outdated kernel headers like Android's. -#define O_SYNC_NEW 04010000 -static const int kAllowedOpenFlags = - O_APPEND | O_ASYNC | O_DIRECT | O_DIRECTORY | O_EXCL | O_LARGEFILE - | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK | O_NDELAY | O_SYNC_NEW - | O_TRUNC | O_CLOEXEC | O_CREAT; -#undef O_SYNC_NEW - -static bool -AllowOpen(int aReqFlags, int aPerms) -{ - if (aReqFlags & ~O_ACCMODE & ~kAllowedOpenFlags) { - return false; - } - int needed; - switch(aReqFlags & O_ACCMODE) { - case O_RDONLY: - needed = SandboxBroker::MAY_READ; - break; - case O_WRONLY: - needed = SandboxBroker::MAY_WRITE; - break; - case O_RDWR: - needed = SandboxBroker::MAY_READ | SandboxBroker::MAY_WRITE; - break; - default: - return false; - } - if (aReqFlags & O_CREAT) { - needed |= SandboxBroker::MAY_CREATE; - } - return (aPerms & needed) == needed; -} - -static int -DoStat(const char* aPath, void* aBuff, int aFlags) -{ - if (aFlags & O_NOFOLLOW) { - return lstatsyscall(aPath, (statstruct*)aBuff); - } - return statsyscall(aPath, (statstruct*)aBuff); -} - -static int -DoLink(const char* aPath, const char* aPath2, - SandboxBrokerCommon::Operation aOper) -{ - if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_LINK) { - return link(aPath, aPath2); - } else if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_SYMLINK) { - return symlink(aPath, aPath2); - } - MOZ_CRASH("SandboxBroker: Unknown link operation"); -} - -size_t -SandboxBroker::ConvertToRealPath(char* aPath, size_t aBufSize, size_t aPathLen) -{ - if (strstr(aPath, "..") != NULL) { - char* result = realpath(aPath, NULL); - if (result != NULL) { - strncpy(aPath, result, aBufSize); - aPath[aBufSize - 1] = '\0'; - free(result); - // Size changed, but guaranteed to be 0 terminated - aPathLen = strlen(aPath); - } - // ValidatePath will handle failure to translate - } - return aPathLen; -} - -void -SandboxBroker::ThreadMain(void) -{ - char threadName[16]; - SprintfLiteral(threadName, "FS Broker %d", mChildPid); - PlatformThread::SetName(threadName); - - // Permissive mode can only be enabled through an environment variable, - // therefore it is sufficient to fetch the value once - // before the main thread loop starts - bool permissive = SandboxInfo::Get().Test(SandboxInfo::kPermissive); - -#ifdef MOZ_WIDGET_GONK -#ifdef __NR_setreuid32 - static const long nr_setreuid = __NR_setreuid32; - static const long nr_setregid = __NR_setregid32; -#else - static const long nr_setreuid = __NR_setreuid; - static const long nr_setregid = __NR_setregid; -#endif - if (syscall(nr_setregid, getgid(), AID_APP + mChildPid) != 0 || - syscall(nr_setreuid, getuid(), AID_APP + mChildPid) != 0) { - MOZ_CRASH("SandboxBroker: failed to drop privileges"); - } -#endif - - while (true) { - struct iovec ios[2]; - // We will receive the path strings in 1 buffer and split them back up. - char recvBuf[2 * (kMaxPathLen + 1)]; - char pathBuf[kMaxPathLen + 1]; - char pathBuf2[kMaxPathLen + 1]; - size_t pathLen; - size_t pathLen2; - char respBuf[kMaxPathLen + 1]; // Also serves as struct stat - Request req; - Response resp; - int respfd; - - // Make sure stat responses fit in the response buffer - MOZ_ASSERT((kMaxPathLen + 1) > sizeof(struct stat)); - - // This makes our string handling below a bit less error prone. - memset(recvBuf, 0, sizeof(recvBuf)); - - ios[0].iov_base = &req; - ios[0].iov_len = sizeof(req); - ios[1].iov_base = recvBuf; - ios[1].iov_len = sizeof(recvBuf); - - const ssize_t recvd = RecvWithFd(mFileDesc, ios, 2, &respfd); - if (recvd == 0) { - if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { - SANDBOX_LOG_ERROR("EOF from pid %d", mChildPid); - } - break; - } - // It could be possible to continue after errors and short reads, - // at least in some cases, but protocol violation indicates a - // hostile client, so terminate the broker instead. - if (recvd < 0) { - SANDBOX_LOG_ERROR("bad read from pid %d: %s", - mChildPid, strerror(errno)); - shutdown(mFileDesc, SHUT_RD); - break; - } - if (recvd < static_cast<ssize_t>(sizeof(req))) { - SANDBOX_LOG_ERROR("bad read from pid %d (%d < %d)", - mChildPid, recvd, sizeof(req)); - shutdown(mFileDesc, SHUT_RD); - break; - } - if (respfd == -1) { - SANDBOX_LOG_ERROR("no response fd from pid %d", mChildPid); - shutdown(mFileDesc, SHUT_RD); - break; - } - - // Initialize the response with the default failure. - memset(&resp, 0, sizeof(resp)); - memset(&respBuf, 0, sizeof(respBuf)); - resp.mError = -EACCES; - ios[0].iov_base = &resp; - ios[0].iov_len = sizeof(resp); - ios[1].iov_base = nullptr; - ios[1].iov_len = 0; - int openedFd = -1; - - // Clear permissions - int perms; - - // Find end of first string, make sure the buffer is still - // 0 terminated. - size_t recvBufLen = static_cast<size_t>(recvd) - sizeof(req); - if (recvBufLen > 0 && recvBuf[recvBufLen - 1] != 0) { - SANDBOX_LOG_ERROR("corrupted path buffer from pid %d", mChildPid); - shutdown(mFileDesc, SHUT_RD); - break; - } - - // First path should fit in maximum path length buffer. - size_t first_len = strlen(recvBuf); - if (first_len <= kMaxPathLen) { - strcpy(pathBuf, recvBuf); - // Skip right over the terminating 0, and try to copy in the - // second path, if any. If there's no path, this will hit a - // 0 immediately (we nulled the buffer before receiving). - // We do not assume the second path is 0-terminated, this is - // enforced below. - strncpy(pathBuf2, recvBuf + first_len + 1, kMaxPathLen + 1); - - // First string is guaranteed to be 0-terminated. - pathLen = first_len; - - // Look up the first pathname but first translate relative paths. - pathLen = ConvertToRealPath(pathBuf, sizeof(pathBuf), pathLen); - perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen)); - - // Same for the second path. - pathLen2 = strnlen(pathBuf2, kMaxPathLen); - if (pathLen2 > 0) { - // Force 0 termination. - pathBuf2[pathLen2] = '\0'; - pathLen2 = ConvertToRealPath(pathBuf2, sizeof(pathBuf2), pathLen2); - int perms2 = mPolicy->Lookup(nsDependentCString(pathBuf2, pathLen2)); - - // Take the intersection of the permissions for both paths. - perms &= perms2; - } - } else { - // Failed to receive intelligible paths. - perms = 0; - } - - // And now perform the operation if allowed. - if (perms & CRASH_INSTEAD) { - // This is somewhat nonmodular, but it works. - resp.mError = -ENOSYS; - } else if (permissive || perms & MAY_ACCESS) { - // If the operation was only allowed because of permissive mode, log it. - if (permissive && !(perms & MAY_ACCESS)) { - AuditPermissive(req.mOp, req.mFlags, perms, pathBuf); - } - - switch(req.mOp) { - case SANDBOX_FILE_OPEN: - if (permissive || AllowOpen(req.mFlags, perms)) { - // Permissions for O_CREAT hardwired to 0600; if that's - // ever a problem we can change the protocol (but really we - // should be trying to remove uses of MAY_CREATE, not add - // new ones). - openedFd = open(pathBuf, req.mFlags | kRequiredOpenFlags, 0600); - if (openedFd >= 0) { - resp.mError = 0; - } else { - resp.mError = -errno; - } - } else { - AuditDenial(req.mOp, req.mFlags, perms, pathBuf); - } - break; - - case SANDBOX_FILE_ACCESS: - if (permissive || AllowAccess(req.mFlags, perms)) { - // This can't use access() itself because that uses the ruid - // and not the euid. In theory faccessat() with AT_EACCESS - // would work, but Linux doesn't actually implement the - // flags != 0 case; glibc has a hack which doesn't even work - // in this case so it'll ignore the flag, and Bionic just - // passes through the syscall and always ignores the flags. - // - // Instead, because we've already checked the requested - // r/w/x bits against the policy, just return success if the - // file exists and hope that's close enough. - if (stat(pathBuf, (struct stat*)&respBuf) == 0) { - resp.mError = 0; - } else { - resp.mError = -errno; - } - } else { - AuditDenial(req.mOp, req.mFlags, perms, pathBuf); - } - break; - - case SANDBOX_FILE_STAT: - if (DoStat(pathBuf, (struct stat*)&respBuf, req.mFlags) == 0) { - resp.mError = 0; - ios[1].iov_base = &respBuf; - ios[1].iov_len = req.mBufSize; - } else { - resp.mError = -errno; - } - break; - - case SANDBOX_FILE_CHMOD: - if (permissive || AllowOperation(W_OK, perms)) { - if (chmod(pathBuf, req.mFlags) == 0) { - resp.mError = 0; - } else { - resp.mError = -errno; - } - } else { - AuditDenial(req.mOp, req.mFlags, perms, pathBuf); - } - break; - - case SANDBOX_FILE_LINK: - case SANDBOX_FILE_SYMLINK: - if (permissive || AllowOperation(W_OK, perms)) { - if (DoLink(pathBuf, pathBuf2, req.mOp) == 0) { - resp.mError = 0; - } else { - resp.mError = -errno; - } - } else { - AuditDenial(req.mOp, req.mFlags, perms, pathBuf); - } - break; - - case SANDBOX_FILE_RENAME: - if (permissive || AllowOperation(W_OK, perms)) { - if (rename(pathBuf, pathBuf2) == 0) { - resp.mError = 0; - } else { - resp.mError = -errno; - } - } else { - AuditDenial(req.mOp, req.mFlags, perms, pathBuf); - } - break; - - case SANDBOX_FILE_MKDIR: - if (permissive || AllowOperation(W_OK | X_OK, perms)) { - if (mkdir(pathBuf, req.mFlags) == 0) { - resp.mError = 0; - } else { - resp.mError = -errno; - } - } else { - AuditDenial(req.mOp, req.mFlags, perms, pathBuf); - } - break; - - case SANDBOX_FILE_UNLINK: - if (permissive || AllowOperation(W_OK, perms)) { - if (unlink(pathBuf) == 0) { - resp.mError = 0; - } else { - resp.mError = -errno; - } - } else { - AuditDenial(req.mOp, req.mFlags, perms, pathBuf); - } - break; - - case SANDBOX_FILE_RMDIR: - if (permissive || AllowOperation(W_OK | X_OK, perms)) { - if (rmdir(pathBuf) == 0) { - resp.mError = 0; - } else { - resp.mError = -errno; - } - } else { - AuditDenial(req.mOp, req.mFlags, perms, pathBuf); - } - break; - - case SANDBOX_FILE_READLINK: - if (permissive || AllowOperation(R_OK, perms)) { - ssize_t respSize = readlink(pathBuf, (char*)&respBuf, sizeof(respBuf)); - if (respSize >= 0) { - resp.mError = respSize; - ios[1].iov_base = &respBuf; - ios[1].iov_len = respSize; - } else { - resp.mError = -errno; - } - } else { - AuditDenial(req.mOp, req.mFlags, perms, pathBuf); - } - break; - } - } else { - MOZ_ASSERT(perms == 0); - AuditDenial(req.mOp, req.mFlags, perms, pathBuf); - } - - const size_t numIO = ios[1].iov_len > 0 ? 2 : 1; - DebugOnly<const ssize_t> sent = SendWithFd(respfd, ios, numIO, openedFd); - close(respfd); - MOZ_ASSERT(sent < 0 || - static_cast<size_t>(sent) == ios[0].iov_len + ios[1].iov_len); - - if (openedFd >= 0) { - close(openedFd); - } - } -} - -void -SandboxBroker::AuditPermissive(int aOp, int aFlags, int aPerms, const char* aPath) -{ - MOZ_RELEASE_ASSERT(SandboxInfo::Get().Test(SandboxInfo::kPermissive)); - - struct stat statBuf; - - if (lstat(aPath, &statBuf) == 0) { - // Path exists, set errno to 0 to indicate "success". - errno = 0; - } - - SANDBOX_LOG_ERROR("SandboxBroker: would have denied op=%d rflags=%o perms=%d path=%s for pid=%d" \ - " permissive=1 error=\"%s\"", aOp, aFlags, aPerms, - aPath, mChildPid, strerror(errno)); -} - -void -SandboxBroker::AuditDenial(int aOp, int aFlags, int aPerms, const char* aPath) -{ - if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { - SANDBOX_LOG_ERROR("SandboxBroker: denied op=%d rflags=%o perms=%d path=%s for pid=%d" \ - " error=\"%s\"", aOp, aFlags, aPerms, aPath, mChildPid, - strerror(errno)); - } -} - - -} // namespace mozilla diff --git a/security/sandbox/linux/broker/SandboxBroker.h b/security/sandbox/linux/broker/SandboxBroker.h deleted file mode 100644 index bb4570a64..000000000 --- a/security/sandbox/linux/broker/SandboxBroker.h +++ /dev/null @@ -1,132 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxBroker_h -#define mozilla_SandboxBroker_h - -#include "mozilla/SandboxBrokerCommon.h" - -#include "base/platform_thread.h" -#include "mozilla/Attributes.h" -#include "mozilla/UniquePtr.h" -#include "nsDataHashtable.h" -#include "nsHashKeys.h" -#include "nsString.h" - -namespace mozilla { - -namespace ipc { -class FileDescriptor; -} - -// This class implements a broker for filesystem operations requested -// by a sandboxed child process -- opening files and accessing their -// metadata. (This is necessary in order to restrict access by path; -// seccomp-bpf can filter only on argument register values, not -// parameters passed in memory like pathnames.) -// -// The broker currently runs on a thread in the parent process (with -// effective uid changed on B2G), which is for memory efficiency -// (compared to forking a process) and simplicity (compared to having -// a separate executable and serializing/deserializing the policy). -// -// See also ../SandboxBrokerClient.h for the corresponding client. - -class SandboxBroker final - : private SandboxBrokerCommon - , public PlatformThread::Delegate -{ - public: - enum Perms { - MAY_ACCESS = 1 << 0, - MAY_READ = 1 << 1, - MAY_WRITE = 1 << 2, - MAY_CREATE = 1 << 3, - // This flag is for testing policy changes -- when the client is - // used with the seccomp-bpf integration, an access to this file - // will invoke a crash dump with the context of the syscall. - // (This overrides all other flags.) - CRASH_INSTEAD = 1 << 4, - // Applies to everything below this path, including subdirs created - // at runtime - RECURSIVE = 1 << 5, - }; - // Bitwise operations on enum values return ints, so just use int in - // the hash table type (and below) to avoid cluttering code with casts. - typedef nsDataHashtable<nsCStringHashKey, int> PathMap; - - class Policy { - PathMap mMap; - public: - Policy(); - Policy(const Policy& aOther); - ~Policy(); - - enum AddCondition { - AddIfExistsNow, - AddAlways, - }; - // Typically, files that don't exist at policy creation time don't - // need to be whitelisted, but this allows adding entries for - // them if they'll exist later. See also the overload below. - void AddPath(int aPerms, const char* aPath, AddCondition aCond); - // This adds all regular files (not directories) in the tree - // rooted at the given path. - void AddTree(int aPerms, const char* aPath); - // A directory, and all files and directories under it, even those - // added after creation (the dir itself must exist). - void AddDir(int aPerms, const char* aPath); - // All files in a directory with a given prefix; useful for devices. - void AddPrefix(int aPerms, const char* aDir, const char* aPrefix); - // Default: add file if it exists when creating policy or if we're - // conferring permission to create it (log files, etc.). - void AddPath(int aPerms, const char* aPath) { - AddPath(aPerms, aPath, - (aPerms & MAY_CREATE) ? AddAlways : AddIfExistsNow); - } - int Lookup(const nsACString& aPath) const; - int Lookup(const char* aPath) const { - return Lookup(nsDependentCString(aPath)); - } - private: - // ValidatePath checks |path| and returns true if these conditions are met - // * Greater than 0 length - // * Is an absolute path - // * No trailing slash - // * No /../ path traversal - bool ValidatePath(const char* path) const; - }; - - // Constructing a broker involves creating a socketpair and a - // background thread to handle requests, so it can fail. If this - // returns nullptr, do not use the value of aClientFdOut. - static UniquePtr<SandboxBroker> - Create(UniquePtr<const Policy> aPolicy, int aChildPid, - ipc::FileDescriptor& aClientFdOut); - virtual ~SandboxBroker(); - - private: - PlatformThreadHandle mThread; - int mFileDesc; - const int mChildPid; - const UniquePtr<const Policy> mPolicy; - - SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid, - int& aClientFd); - void ThreadMain(void) override; - void AuditPermissive(int aOp, int aFlags, int aPerms, const char* aPath); - void AuditDenial(int aOp, int aFlags, int aPerms, const char* aPath); - // Remap relative paths to absolute paths. - size_t ConvertToRealPath(char* aPath, size_t aBufSize, size_t aPathLen); - - // Holding a UniquePtr should disallow copying, but to make that explicit: - SandboxBroker(const SandboxBroker&) = delete; - void operator=(const SandboxBroker&) = delete; -}; - -} // namespace mozilla - -#endif // mozilla_SandboxBroker_h diff --git a/security/sandbox/linux/broker/SandboxBrokerCommon.cpp b/security/sandbox/linux/broker/SandboxBrokerCommon.cpp deleted file mode 100644 index fe7bc8c45..000000000 --- a/security/sandbox/linux/broker/SandboxBrokerCommon.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* -*- 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 "SandboxBrokerCommon.h" - -#include "mozilla/Assertions.h" - -#include <errno.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#ifndef MSG_CMSG_CLOEXEC -#ifdef XP_LINUX -// As always, Android's kernel headers are somewhat old. -#define MSG_CMSG_CLOEXEC 0x40000000 -#else -// Most of this code can support other POSIX OSes, but being able to -// receive fds and atomically make them close-on-exec is important, -// because this is running in a multithreaded process that can fork. -// In the future, if the broker becomes a dedicated executable, this -// can change. -#error "No MSG_CMSG_CLOEXEC?" -#endif // XP_LINUX -#endif // MSG_CMSG_CLOEXEC - -namespace mozilla { - -/* static */ ssize_t -SandboxBrokerCommon::RecvWithFd(int aFd, const iovec* aIO, size_t aNumIO, - int* aPassedFdPtr) -{ - struct msghdr msg = {}; - msg.msg_iov = const_cast<iovec*>(aIO); - msg.msg_iovlen = aNumIO; - - char cmsg_buf[CMSG_SPACE(sizeof(int))]; - if (aPassedFdPtr) { - msg.msg_control = cmsg_buf; - msg.msg_controllen = sizeof(cmsg_buf); - *aPassedFdPtr = -1; - } - - ssize_t rv; - do { - // MSG_CMSG_CLOEXEC is needed to prevent the parent process from - // accidentally leaking a copy of the child's response socket to a - // new child process. (The child won't be able to exec, so this - // doesn't matter as much for that direction.) - rv = recvmsg(aFd, &msg, MSG_CMSG_CLOEXEC); - } while (rv < 0 && errno == EINTR); - - if (rv <= 0) { - return rv; - } - if (msg.msg_controllen > 0) { - MOZ_ASSERT(aPassedFdPtr); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS) { - int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); - if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { - // A client could, for example, send an extra 32-bit int if - // CMSG_SPACE pads to 64-bit size_t alignment. If so, treat - // it as an error, but also don't leak the fds. - for (size_t i = 0; CMSG_LEN(sizeof(int) * i) < cmsg->cmsg_len; ++i) { - close(fds[i]); - } - errno = EMSGSIZE; - return -1; - } - *aPassedFdPtr = fds[0]; - } else { - errno = EPROTO; - return -1; - } - } - if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) { - if (aPassedFdPtr && *aPassedFdPtr >= 0) { - close(*aPassedFdPtr); - *aPassedFdPtr = -1; - } - errno = EMSGSIZE; - return -1; - } - - return rv; -} - -/* static */ ssize_t -SandboxBrokerCommon::SendWithFd(int aFd, const iovec* aIO, size_t aNumIO, - int aPassedFd) -{ - struct msghdr msg = {}; - msg.msg_iov = const_cast<iovec*>(aIO); - msg.msg_iovlen = aNumIO; - - char cmsg_buf[CMSG_SPACE(sizeof(int))]; - if (aPassedFd != -1) { - msg.msg_control = cmsg_buf; - msg.msg_controllen = sizeof(cmsg_buf); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = aPassedFd; - } - - ssize_t rv; - do { - rv = sendmsg(aFd, &msg, MSG_NOSIGNAL); - } while (rv < 0 && errno == EINTR); - - return rv; -} - -} // namespace mozilla diff --git a/security/sandbox/linux/broker/SandboxBrokerCommon.h b/security/sandbox/linux/broker/SandboxBrokerCommon.h deleted file mode 100644 index dbd17e0b9..000000000 --- a/security/sandbox/linux/broker/SandboxBrokerCommon.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxBrokerCommon_h -#define mozilla_SandboxBrokerCommon_h - -#include <sys/types.h> - -struct iovec; - -// This file defines the protocol between the filesystem broker, -// described in SandboxBroker.h, and its client, described in -// ../SandboxBrokerClient.h; and it defines some utility functions -// used by both. -// -// In order to keep the client simple while allowing it to be thread -// safe and async signal safe, the main broker socket is used only for -// requests; responses arrive on a per-request socketpair sent with -// the request. (This technique is also used by Chromium and Breakpad.) - -namespace mozilla { - -class SandboxBrokerCommon { -public: - enum Operation { - SANDBOX_FILE_OPEN, - SANDBOX_FILE_ACCESS, - SANDBOX_FILE_STAT, - SANDBOX_FILE_CHMOD, - SANDBOX_FILE_LINK, - SANDBOX_FILE_SYMLINK, - SANDBOX_FILE_MKDIR, - SANDBOX_FILE_RENAME, - SANDBOX_FILE_RMDIR, - SANDBOX_FILE_UNLINK, - SANDBOX_FILE_READLINK, - }; - - struct Request { - Operation mOp; - // For open, flags; for access, "mode"; for stat, O_NOFOLLOW for lstat. - int mFlags; - // Size of return value buffer, if any - size_t mBufSize; - // The rest of the packet is the pathname. - // SCM_RIGHTS for response socket attached. - }; - - struct Response { - // Syscall result, -errno if failure, or 0 for no error - int mError; - // Followed by struct stat for stat/lstat. - // SCM_RIGHTS attached for successful open. - }; - - // This doesn't need to be the system's maximum path length, just - // the largest path that would be allowed by any policy. (It's used - // to size a stack-allocated buffer.) - static const size_t kMaxPathLen = 4096; - - static ssize_t RecvWithFd(int aFd, const iovec* aIO, size_t aNumIO, - int* aPassedFdPtr); - static ssize_t SendWithFd(int aFd, const iovec* aIO, size_t aNumIO, - int aPassedFd); -}; - -} // namespace mozilla - -#endif // mozilla_SandboxBrokerCommon_h diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp deleted file mode 100644 index c3a15ea3d..000000000 --- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- 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 "SandboxBrokerPolicyFactory.h" -#include "SandboxInfo.h" - -#include "mozilla/ClearOnShutdown.h" -#include "mozilla/Preferences.h" -#include "nsPrintfCString.h" -#include "nsString.h" -#include "nsThreadUtils.h" -#include "nsXULAppAPI.h" -#include "SpecialSystemDirectory.h" - -#ifdef ANDROID -#include "cutils/properties.h" -#endif - -namespace mozilla { - -/* static */ bool -SandboxBrokerPolicyFactory::IsSystemSupported() { -#ifdef ANDROID - char hardware[PROPERTY_VALUE_MAX]; - int length = property_get("ro.hardware", hardware, nullptr); - // "goldfish" -> emulator. Other devices can be added when we're - // reasonably sure they work. Eventually this won't be needed.... - if (length > 0 && strcmp(hardware, "goldfish") == 0) { - return true; - } - - // When broker is running in permissive mode, we enable it - // automatically regardless of the device. - if (SandboxInfo::Get().Test(SandboxInfo::kPermissive)) { - return true; - } -#endif - return false; -} - -SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory() -{ - // Policy entries that are the same in every process go here, and - // are cached over the lifetime of the factory. -} - -} // namespace mozilla diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h deleted file mode 100644 index bf9be9856..000000000 --- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_SandboxBrokerPolicyFactory_h -#define mozilla_SandboxBrokerPolicyFactory_h - -#include "mozilla/SandboxBroker.h" - -namespace mozilla { - -class SandboxBrokerPolicyFactory { -public: - SandboxBrokerPolicyFactory(); - -private: - UniquePtr<const SandboxBroker::Policy> mCommonContentPolicy; - // B2G devices tend to have hardware-specific paths used by device - // drivers, so rollout of filesystem isolation will need per-device - // testing. This predicate allows that to happen gradually. - static bool IsSystemSupported(); -}; - -} // namespace mozilla - -#endif // mozilla_SandboxBrokerPolicyFactory_h diff --git a/security/sandbox/linux/broker/SandboxBrokerUtils.h b/security/sandbox/linux/broker/SandboxBrokerUtils.h deleted file mode 100644 index 1db4f4411..000000000 --- a/security/sandbox/linux/broker/SandboxBrokerUtils.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- 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/. */ -#ifndef mozilla_SandboxBrokerUtils_h -#define mozilla_SandboxBrokerUtils_h - -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include "sandbox/linux/system_headers/linux_syscalls.h" - -// On 32-bit Linux, stat calls are translated by libc into stat64 -// calls. We'll intercept those and handle them in the stat functions -// but must be sure to use the right structure layout. - -#if defined(__NR_stat64) -typedef struct stat64 statstruct; -#define statsyscall stat64 -#define lstatsyscall lstat64 -#elif defined(__NR_stat) -typedef struct stat statstruct; -#define statsyscall stat -#define lstatsyscall lstat -#else -#error Missing stat syscall include. -#endif - -#endif // mozilla_SandboxBrokerUtils_h diff --git a/security/sandbox/linux/broker/moz.build b/security/sandbox/linux/broker/moz.build deleted file mode 100644 index 343a5cfad..000000000 --- a/security/sandbox/linux/broker/moz.build +++ /dev/null @@ -1,37 +0,0 @@ -# -*- Mode: python; python-indent: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS.mozilla += [ - 'SandboxBroker.h', - 'SandboxBrokerCommon.h', - 'SandboxBrokerPolicyFactory.h', -] - -SOURCES += [ - 'SandboxBroker.cpp', - 'SandboxBrokerCommon.cpp', - 'SandboxBrokerPolicyFactory.cpp', -] - -if CONFIG['OS_TARGET'] == 'Android': - if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': - DEFINES['HAVE_ANDROID_OS'] = True - -LOCAL_INCLUDES += [ - '/security/sandbox/linux', # SandboxLogging.h, SandboxInfo.h -] - -# Need this for mozilla::ipc::FileDescriptor etc. -include('/ipc/chromium/chromium-config.mozbuild') - -# Need this for safe_sprintf.h used by SandboxLogging.h, -# but it has to be after ipc/chromium/src. -LOCAL_INCLUDES += [ - '/security/sandbox/chromium', -] - - -FINAL_LIBRARY = 'xul' diff --git a/security/sandbox/linux/glue/SandboxCrash.cpp b/security/sandbox/linux/glue/SandboxCrash.cpp deleted file mode 100644 index 8ead16bdf..000000000 --- a/security/sandbox/linux/glue/SandboxCrash.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* -*- 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/. */ - -// This file needs to be linked into libxul, so it can access the JS -// stack and the crash reporter. Everything else in this directory -// should be able to be linked into its own shared library, in order -// to be able to isolate sandbox/chromium from ipc/chromium. - -#include "SandboxInternal.h" -#include "SandboxLogging.h" - -#include <unistd.h> -#include <sys/syscall.h> - -#include "mozilla/Unused.h" -#include "mozilla/dom/Exceptions.h" -#include "nsContentUtils.h" -#include "mozilla/StackWalk.h" -#include "nsString.h" -#include "nsThreadUtils.h" - -namespace mozilla { - -// Log JS stack info in the same place as the sandbox violation -// message. Useful in case the responsible code is JS and all we have -// are logs and a minidump with the C++ stacks (e.g., on TBPL). -static void -SandboxLogJSStack(void) -{ - if (!NS_IsMainThread()) { - // This might be a worker thread... or it might be a non-JS - // thread, or a non-NSPR thread. There's isn't a good API for - // dealing with this, yet. - return; - } - if (!nsContentUtils::XPConnect()) { - // There is no content (e.g., the process is a media plugin), in - // which case this will probably crash and definitely not work. - return; - } - nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack(); - // If we got a stack, we must have a current JSContext. This is icky. :( - // Would be better if GetCurrentJSStack() handed out the JSContext it ended up - // using or something. - JSContext* cx = frame ? nsContentUtils::GetCurrentJSContext() : nullptr; - for (int i = 0; frame != nullptr; ++i) { - nsAutoString fileName, funName; - int32_t lineNumber; - - // Don't stop unwinding if an attribute can't be read. - fileName.SetIsVoid(true); - Unused << frame->GetFilename(cx, fileName); - lineNumber = 0; - Unused << frame->GetLineNumber(cx, &lineNumber); - funName.SetIsVoid(true); - Unused << frame->GetName(cx, funName); - - if (!funName.IsVoid() || !fileName.IsVoid()) { - SANDBOX_LOG_ERROR("JS frame %d: %s %s line %d", i, - funName.IsVoid() ? - "(anonymous)" : NS_ConvertUTF16toUTF8(funName).get(), - fileName.IsVoid() ? - "(no file)" : NS_ConvertUTF16toUTF8(fileName).get(), - lineNumber); - } - - nsCOMPtr<nsIStackFrame> nextFrame; - nsresult rv = frame->GetCaller(cx, getter_AddRefs(nextFrame)); - NS_ENSURE_SUCCESS_VOID(rv); - frame = nextFrame; - } -} - -static void SandboxPrintStackFrame(uint32_t aFrameNumber, void *aPC, void *aSP, - void *aClosure) -{ - char buf[1024]; - MozCodeAddressDetails details; - - MozDescribeCodeAddress(aPC, &details); - MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details); - SANDBOX_LOG_ERROR("frame %s", buf); -} - -static void -SandboxLogCStack() -{ - // Skip 3 frames: one for this module, one for the signal handler in - // libmozsandbox, and one for the signal trampoline. - // - // Warning: this might not print any stack frames. MozStackWalk - // can't walk past the signal trampoline on ARM (bug 968531), and - // x86 frame pointer walking may or may not work (bug 1082276). - - MozStackWalk(SandboxPrintStackFrame, /* skip */ 3, /* max */ 0, - nullptr, 0, nullptr); - SANDBOX_LOG_ERROR("end of stack."); -} - -static void -SandboxCrash(int nr, siginfo_t *info, void *void_context) -{ - pid_t pid = getpid(), tid = syscall(__NR_gettid); - bool dumped = false; - - if (!dumped) { - SANDBOX_LOG_ERROR("crash reporter is disabled (or failed);" - " trying stack trace:"); - SandboxLogCStack(); - } - - // Do this last, in case it crashes or deadlocks. - SandboxLogJSStack(); - - // Try to reraise, so the parent sees that this process crashed. - // (If tgkill is forbidden, then seccomp will raise SIGSYS, which - // also accomplishes that goal.) - signal(SIGSYS, SIG_DFL); - syscall(__NR_tgkill, pid, tid, nr); -} - -static void __attribute__((constructor)) -SandboxSetCrashFunc() -{ - gSandboxCrashFunc = SandboxCrash; -} - -} // namespace mozilla diff --git a/security/sandbox/linux/glue/moz.build b/security/sandbox/linux/glue/moz.build deleted file mode 100644 index 0d40dcd63..000000000 --- a/security/sandbox/linux/glue/moz.build +++ /dev/null @@ -1,29 +0,0 @@ -# -*- Mode: python; python-indent: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -SOURCES += [ - '../../chromium/base/strings/safe_sprintf.cc', - '../SandboxLogging.cpp', - 'SandboxCrash.cpp', -] - -# Avoid Chromium logging dependency, because this is going into -# libxul. See also the comment in SandboxLogging.h. -SOURCES['../../chromium/base/strings/safe_sprintf.cc'].flags += ['-DNDEBUG'] - -LOCAL_INCLUDES += [ - '/security/sandbox/chromium', - '/security/sandbox/linux', -] - -USE_LIBS += [ - 'mozsandbox', -] - -FINAL_LIBRARY = 'xul' - -if CONFIG['GNU_CXX']: - CXXFLAGS += ['-Wno-error=shadow'] diff --git a/security/sandbox/linux/gtest/TestBroker.cpp b/security/sandbox/linux/gtest/TestBroker.cpp deleted file mode 100644 index a311e181a..000000000 --- a/security/sandbox/linux/gtest/TestBroker.cpp +++ /dev/null @@ -1,626 +0,0 @@ -/* -*- 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 "gtest/gtest.h" - -#include "broker/SandboxBroker.h" -#include "broker/SandboxBrokerUtils.h" -#include "SandboxBrokerClient.h" - -#include <errno.h> -#include <fcntl.h> -#include <pthread.h> -#include <stdlib.h> -#include <sched.h> -#include <semaphore.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -#include "mozilla/Atomics.h" -#include "mozilla/NullPtr.h" -#include "mozilla/PodOperations.h" -#include "mozilla/UniquePtr.h" -#include "mozilla/ipc/FileDescriptor.h" - -namespace mozilla { - -static const int MAY_ACCESS = SandboxBroker::MAY_ACCESS; -static const int MAY_READ = SandboxBroker::MAY_READ; -static const int MAY_WRITE = SandboxBroker::MAY_WRITE; -static const int MAY_CREATE = SandboxBroker::MAY_CREATE; -static const auto AddAlways = SandboxBroker::Policy::AddAlways; - -class SandboxBrokerTest : public ::testing::Test -{ - UniquePtr<SandboxBroker> mServer; - UniquePtr<SandboxBrokerClient> mClient; - - UniquePtr<const SandboxBroker::Policy> GetPolicy() const; - - template<class C, void (C::* Main)()> - static void* ThreadMain(void* arg) { - (static_cast<C*>(arg)->*Main)(); - return nullptr; - } - -protected: - int Open(const char* aPath, int aFlags) { - return mClient->Open(aPath, aFlags); - } - int Access(const char* aPath, int aMode) { - return mClient->Access(aPath, aMode); - } - int Stat(const char* aPath, statstruct* aStat) { - return mClient->Stat(aPath, aStat); - } - int LStat(const char* aPath, statstruct* aStat) { - return mClient->LStat(aPath, aStat); - } - int Chmod(const char* aPath, int aMode) { - return mClient->Chmod(aPath, aMode); - } - int Link(const char* aPath, const char* bPath) { - return mClient->Link(aPath, bPath); - } - int Mkdir(const char* aPath, int aMode) { - return mClient->Mkdir(aPath, aMode); - } - int Symlink(const char* aPath, const char* bPath) { - return mClient->Symlink(aPath, bPath); - } - int Rename(const char* aPath, const char* bPath) { - return mClient->Rename(aPath, bPath); - } - int Rmdir(const char* aPath) { - return mClient->Rmdir(aPath); - } - int Unlink(const char* aPath) { - return mClient->Unlink(aPath); - } - ssize_t Readlink(const char* aPath, char* aBuff, size_t aSize) { - return mClient->Readlink(aPath, aBuff, aSize); - } - - virtual void SetUp() { - ipc::FileDescriptor fd; - - mServer = SandboxBroker::Create(GetPolicy(), getpid(), fd); - ASSERT_NE(mServer, nullptr); - ASSERT_TRUE(fd.IsValid()); - auto rawFD = fd.ClonePlatformHandle(); - mClient.reset(new SandboxBrokerClient(rawFD.release())); - } - - template<class C, void (C::* Main)()> - void StartThread(pthread_t *aThread) { - ASSERT_EQ(0, pthread_create(aThread, nullptr, ThreadMain<C, Main>, - static_cast<C*>(this))); - } - - template<class C, void (C::* Main)()> - void RunOnManyThreads() { - static const int kNumThreads = 5; - pthread_t threads[kNumThreads]; - for (int i = 0; i < kNumThreads; ++i) { - StartThread<C, Main>(&threads[i]); - } - for (int i = 0; i < kNumThreads; ++i) { - void* retval; - ASSERT_EQ(pthread_join(threads[i], &retval), 0); - ASSERT_EQ(retval, static_cast<void*>(nullptr)); - } - } - -public: - void MultiThreadOpenWorker(); - void MultiThreadStatWorker(); -}; - -UniquePtr<const SandboxBroker::Policy> -SandboxBrokerTest::GetPolicy() const -{ - UniquePtr<SandboxBroker::Policy> policy(new SandboxBroker::Policy()); - - policy->AddPath(MAY_READ | MAY_WRITE, "/dev/null", AddAlways); - policy->AddPath(MAY_READ, "/dev/zero", AddAlways); - policy->AddPath(MAY_READ, "/var/empty/qwertyuiop", AddAlways); - policy->AddPath(MAY_ACCESS, "/proc/self", AddAlways); // Warning: Linux-specific. - policy->AddPath(MAY_READ | MAY_WRITE, "/tmp", AddAlways); - policy->AddPath(MAY_READ | MAY_WRITE | MAY_CREATE, "/tmp/blublu", AddAlways); - policy->AddPath(MAY_READ | MAY_WRITE | MAY_CREATE, "/tmp/blublublu", AddAlways); - - return Move(policy); -} - -TEST_F(SandboxBrokerTest, OpenForRead) -{ - int fd; - - fd = Open("/dev/null", O_RDONLY); - ASSERT_GE(fd, 0) << "Opening /dev/null failed."; - close(fd); - fd = Open("/dev/zero", O_RDONLY); - ASSERT_GE(fd, 0) << "Opening /dev/zero failed."; - close(fd); - fd = Open("/var/empty/qwertyuiop", O_RDONLY); - EXPECT_EQ(-ENOENT, fd) << "Opening allowed but nonexistent file succeeded."; - fd = Open("/proc/self", O_RDONLY); - EXPECT_EQ(-EACCES, fd) << "Opening stat-only file for read succeeded."; - fd = Open("/proc/self/stat", O_RDONLY); - EXPECT_EQ(-EACCES, fd) << "Opening disallowed file succeeded."; -} - -TEST_F(SandboxBrokerTest, OpenForWrite) -{ - int fd; - - fd = Open("/dev/null", O_WRONLY); - ASSERT_GE(fd, 0) << "Opening /dev/null write-only failed."; - close(fd); - fd = Open("/dev/null", O_RDWR); - ASSERT_GE(fd, 0) << "Opening /dev/null read/write failed."; - close(fd); - fd = Open("/dev/zero", O_WRONLY); - ASSERT_EQ(-EACCES, fd) << "Opening read-only-by-policy file write-only succeeded."; - fd = Open("/dev/zero", O_RDWR); - ASSERT_EQ(-EACCES, fd) << "Opening read-only-by-policy file read/write succeeded."; -} - -TEST_F(SandboxBrokerTest, SimpleRead) -{ - int fd; - char c; - - fd = Open("/dev/null", O_RDONLY); - ASSERT_GE(fd, 0); - EXPECT_EQ(0, read(fd, &c, 1)); - close(fd); - fd = Open("/dev/zero", O_RDONLY); - ASSERT_GE(fd, 0); - ASSERT_EQ(1, read(fd, &c, 1)); - EXPECT_EQ(c, '\0'); -} - -TEST_F(SandboxBrokerTest, Access) -{ - EXPECT_EQ(0, Access("/dev/null", F_OK)); - EXPECT_EQ(0, Access("/dev/null", R_OK)); - EXPECT_EQ(0, Access("/dev/null", W_OK)); - EXPECT_EQ(0, Access("/dev/null", R_OK|W_OK)); - EXPECT_EQ(-EACCES, Access("/dev/null", X_OK)); - EXPECT_EQ(-EACCES, Access("/dev/null", R_OK|X_OK)); - - EXPECT_EQ(0, Access("/dev/zero", R_OK)); - EXPECT_EQ(-EACCES, Access("/dev/zero", W_OK)); - EXPECT_EQ(-EACCES, Access("/dev/zero", R_OK|W_OK)); - - EXPECT_EQ(-ENOENT, Access("/var/empty/qwertyuiop", R_OK)); - EXPECT_EQ(-EACCES, Access("/var/empty/qwertyuiop", W_OK)); - - EXPECT_EQ(0, Access("/proc/self", F_OK)); - EXPECT_EQ(-EACCES, Access("/proc/self", R_OK)); - - EXPECT_EQ(-EACCES, Access("/proc/self/stat", F_OK)); -} - -TEST_F(SandboxBrokerTest, Stat) -{ - statstruct realStat, brokeredStat; - ASSERT_EQ(0, statsyscall("/dev/null", &realStat)) << "Shouldn't ever fail!"; - EXPECT_EQ(0, Stat("/dev/null", &brokeredStat)); - EXPECT_EQ(realStat.st_ino, brokeredStat.st_ino); - EXPECT_EQ(realStat.st_rdev, brokeredStat.st_rdev); - - EXPECT_EQ(-ENOENT, Stat("/var/empty/qwertyuiop", &brokeredStat)); - EXPECT_EQ(-EACCES, Stat("/dev", &brokeredStat)); - - EXPECT_EQ(0, Stat("/proc/self", &brokeredStat)); - EXPECT_TRUE(S_ISDIR(brokeredStat.st_mode)); -} - -TEST_F(SandboxBrokerTest, LStat) -{ - statstruct realStat, brokeredStat; - ASSERT_EQ(0, lstatsyscall("/dev/null", &realStat)); - EXPECT_EQ(0, LStat("/dev/null", &brokeredStat)); - EXPECT_EQ(realStat.st_ino, brokeredStat.st_ino); - EXPECT_EQ(realStat.st_rdev, brokeredStat.st_rdev); - - EXPECT_EQ(-ENOENT, LStat("/var/empty/qwertyuiop", &brokeredStat)); - EXPECT_EQ(-EACCES, LStat("/dev", &brokeredStat)); - - EXPECT_EQ(0, LStat("/proc/self", &brokeredStat)); - EXPECT_TRUE(S_ISLNK(brokeredStat.st_mode)); -} - -static void PrePostTestCleanup(void) -{ - unlink("/tmp/blublu"); - rmdir("/tmp/blublu"); - unlink("/tmp/nope"); - rmdir("/tmp/nope"); - unlink("/tmp/blublublu"); - rmdir("/tmp/blublublu"); -} - -TEST_F(SandboxBrokerTest, Chmod) -{ - PrePostTestCleanup(); - - int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT); - ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed."; - close(fd); - // Set read only. SandboxBroker enforces 0600 mode flags. - ASSERT_EQ(0, Chmod("/tmp/blublu", S_IRUSR)); - // SandboxBroker doesn't use real access(), it just checks against - // the policy. So it can't see the change in permisions here. - // This won't work: - // EXPECT_EQ(-EACCES, Access("/tmp/blublu", W_OK)); - statstruct realStat; - EXPECT_EQ(0, statsyscall("/tmp/blublu", &realStat)); - EXPECT_EQ((mode_t)S_IRUSR, realStat.st_mode & 0777); - - ASSERT_EQ(0, Chmod("/tmp/blublu", S_IRUSR | S_IWUSR)); - EXPECT_EQ(0, statsyscall("/tmp/blublu", &realStat)); - EXPECT_EQ((mode_t)(S_IRUSR | S_IWUSR), realStat.st_mode & 0777); - EXPECT_EQ(0, unlink("/tmp/blublu")); - - PrePostTestCleanup(); -} - -TEST_F(SandboxBrokerTest, Link) -{ - PrePostTestCleanup(); - - int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT); - ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed."; - close(fd); - ASSERT_EQ(0, Link("/tmp/blublu", "/tmp/blublublu")); - EXPECT_EQ(0, Access("/tmp/blublublu", F_OK)); - // Not whitelisted target path - EXPECT_EQ(-EACCES, Link("/tmp/blublu", "/tmp/nope")); - EXPECT_EQ(0, unlink("/tmp/blublublu")); - EXPECT_EQ(0, unlink("/tmp/blublu")); - - PrePostTestCleanup(); -} - -TEST_F(SandboxBrokerTest, Symlink) -{ - PrePostTestCleanup(); - - int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT); - ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed."; - close(fd); - ASSERT_EQ(0, Symlink("/tmp/blublu", "/tmp/blublublu")); - EXPECT_EQ(0, Access("/tmp/blublublu", F_OK)); - statstruct aStat; - ASSERT_EQ(0, lstatsyscall("/tmp/blublublu", &aStat)); - EXPECT_EQ((mode_t)S_IFLNK, aStat.st_mode & S_IFMT); - // Not whitelisted target path - EXPECT_EQ(-EACCES, Symlink("/tmp/blublu", "/tmp/nope")); - EXPECT_EQ(0, unlink("/tmp/blublublu")); - EXPECT_EQ(0, unlink("/tmp/blublu")); - - PrePostTestCleanup(); -} - -TEST_F(SandboxBrokerTest, Mkdir) -{ - PrePostTestCleanup(); - - ASSERT_EQ(0, mkdir("/tmp/blublu", 0600)) - << "Creating dir /tmp/blublu failed."; - EXPECT_EQ(0, Access("/tmp/blublu", F_OK)); - // Not whitelisted target path - EXPECT_EQ(-EACCES, Mkdir("/tmp/nope", 0600)) - << "Creating dir without MAY_CREATE succeed."; - EXPECT_EQ(0, rmdir("/tmp/blublu")); - - PrePostTestCleanup(); -} - -TEST_F(SandboxBrokerTest, Rename) -{ - PrePostTestCleanup(); - - ASSERT_EQ(0, mkdir("/tmp/blublu", 0600)) - << "Creating dir /tmp/blublu failed."; - EXPECT_EQ(0, Access("/tmp/blublu", F_OK)); - ASSERT_EQ(0, Rename("/tmp/blublu", "/tmp/blublublu")); - EXPECT_EQ(0, Access("/tmp/blublublu", F_OK)); - EXPECT_EQ(-ENOENT , Access("/tmp/blublu", F_OK)); - // Not whitelisted target path - EXPECT_EQ(-EACCES, Rename("/tmp/blublublu", "/tmp/nope")) - << "Renaming dir without write access succeed."; - EXPECT_EQ(0, rmdir("/tmp/blublublu")); - - PrePostTestCleanup(); -} - -TEST_F(SandboxBrokerTest, Rmdir) -{ - PrePostTestCleanup(); - - ASSERT_EQ(0, mkdir("/tmp/blublu", 0600)) - << "Creating dir /tmp/blublu failed."; - EXPECT_EQ(0, Access("/tmp/blublu", F_OK)); - ASSERT_EQ(0, Rmdir("/tmp/blublu")); - EXPECT_EQ(-ENOENT, Access("/tmp/blublu", F_OK)); - // Bypass sandbox to create a non-deletable dir - ASSERT_EQ(0, mkdir("/tmp/nope", 0600)); - EXPECT_EQ(-EACCES, Rmdir("/tmp/nope")); - - PrePostTestCleanup(); -} - -TEST_F(SandboxBrokerTest, Unlink) -{ - PrePostTestCleanup(); - - int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT); - ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed."; - close(fd); - EXPECT_EQ(0, Access("/tmp/blublu", F_OK)); - EXPECT_EQ(0, Unlink("/tmp/blublu")); - EXPECT_EQ(-ENOENT , Access("/tmp/blublu", F_OK)); - // Bypass sandbox to write a non-deletable file - fd = open("/tmp/nope", O_WRONLY | O_CREAT, 0600); - ASSERT_GE(fd, 0) << "Opening /tmp/nope for writing failed."; - close(fd); - EXPECT_EQ(-EACCES, Unlink("/tmp/nope")); - - PrePostTestCleanup(); -} - -TEST_F(SandboxBrokerTest, Readlink) -{ - PrePostTestCleanup(); - - int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT); - ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed."; - close(fd); - ASSERT_EQ(0, Symlink("/tmp/blublu", "/tmp/blublublu")); - EXPECT_EQ(0, Access("/tmp/blublublu", F_OK)); - char linkBuff[256]; - EXPECT_EQ(11, Readlink("/tmp/blublublu", linkBuff, sizeof(linkBuff))); - linkBuff[11] = '\0'; - EXPECT_EQ(0, strcmp(linkBuff, "/tmp/blublu")); - - PrePostTestCleanup(); -} - -TEST_F(SandboxBrokerTest, MultiThreadOpen) { - RunOnManyThreads<SandboxBrokerTest, - &SandboxBrokerTest::MultiThreadOpenWorker>(); -} -void SandboxBrokerTest::MultiThreadOpenWorker() { - static const int kNumLoops = 10000; - - for (int i = 1; i <= kNumLoops; ++i) { - int nullfd = Open("/dev/null", O_RDONLY); - int zerofd = Open("/dev/zero", O_RDONLY); - ASSERT_GE(nullfd, 0) << "Loop " << i << "/" << kNumLoops; - ASSERT_GE(zerofd, 0) << "Loop " << i << "/" << kNumLoops; - char c; - ASSERT_EQ(0, read(nullfd, &c, 1)) << "Loop " << i << "/" << kNumLoops; - ASSERT_EQ(1, read(zerofd, &c, 1)) << "Loop " << i << "/" << kNumLoops; - ASSERT_EQ('\0', c) << "Loop " << i << "/" << kNumLoops; - close(nullfd); - close(zerofd); - } -} - -TEST_F(SandboxBrokerTest, MultiThreadStat) { - RunOnManyThreads<SandboxBrokerTest, - &SandboxBrokerTest::MultiThreadStatWorker>(); -} -void SandboxBrokerTest::MultiThreadStatWorker() { - static const int kNumLoops = 7500; - statstruct nullStat, zeroStat, selfStat; - dev_t realNullDev, realZeroDev; - ino_t realSelfInode; - - ASSERT_EQ(0, statsyscall("/dev/null", &nullStat)) << "Shouldn't ever fail!"; - ASSERT_EQ(0, statsyscall("/dev/zero", &zeroStat)) << "Shouldn't ever fail!"; - ASSERT_EQ(0, lstatsyscall("/proc/self", &selfStat)) << "Shouldn't ever fail!"; - ASSERT_TRUE(S_ISLNK(selfStat.st_mode)) << "Shouldn't ever fail!"; - realNullDev = nullStat.st_rdev; - realZeroDev = zeroStat.st_rdev; - realSelfInode = selfStat.st_ino; - for (int i = 1; i <= kNumLoops; ++i) { - ASSERT_EQ(0, Stat("/dev/null", &nullStat)) - << "Loop " << i << "/" << kNumLoops; - ASSERT_EQ(0, Stat("/dev/zero", &zeroStat)) - << "Loop " << i << "/" << kNumLoops; - ASSERT_EQ(0, LStat("/proc/self", &selfStat)) - << "Loop " << i << "/" << kNumLoops; - - ASSERT_EQ(realNullDev, nullStat.st_rdev) - << "Loop " << i << "/" << kNumLoops; - ASSERT_EQ(realZeroDev, zeroStat.st_rdev) - << "Loop " << i << "/" << kNumLoops; - ASSERT_TRUE(S_ISLNK(selfStat.st_mode)) - << "Loop " << i << "/" << kNumLoops; - ASSERT_EQ(realSelfInode, selfStat.st_ino) - << "Loop " << i << "/" << kNumLoops; - } -} - -#if 0 -class SandboxBrokerSigStress : public SandboxBrokerTest -{ - int mSigNum; - struct sigaction mOldAction; - Atomic<void*> mVoidPtr; - - static void SigHandler(int aSigNum, siginfo_t* aSigInfo, void *aCtx) { - ASSERT_EQ(SI_QUEUE, aSigInfo->si_code); - SandboxBrokerSigStress* that = - static_cast<SandboxBrokerSigStress*>(aSigInfo->si_value.sival_ptr); - ASSERT_EQ(that->mSigNum, aSigNum); - that->DoSomething(); - } - -protected: - Atomic<int> mTestIter; - sem_t mSemaphore; - - void SignalThread(pthread_t aThread) { - union sigval sv; - sv.sival_ptr = this; - ASSERT_NE(0, mSigNum); - ASSERT_EQ(0, pthread_sigqueue(aThread, mSigNum, sv)); - } - - virtual void SetUp() { - ASSERT_EQ(0, sem_init(&mSemaphore, 0, 0)); - mVoidPtr = nullptr; - mSigNum = 0; - for (int sigNum = SIGRTMIN; sigNum < SIGRTMAX; ++sigNum) { - ASSERT_EQ(0, sigaction(sigNum, nullptr, &mOldAction)); - if ((mOldAction.sa_flags & SA_SIGINFO) == 0 && - mOldAction.sa_handler == SIG_DFL) { - struct sigaction newAction; - PodZero(&newAction); - newAction.sa_flags = SA_SIGINFO; - newAction.sa_sigaction = SigHandler; - ASSERT_EQ(0, sigaction(sigNum, &newAction, nullptr)); - mSigNum = sigNum; - break; - } - } - ASSERT_NE(mSigNum, 0); - - SandboxBrokerTest::SetUp(); - } - - virtual void TearDown() { - ASSERT_EQ(0, sem_destroy(&mSemaphore)); - if (mSigNum != 0) { - ASSERT_EQ(0, sigaction(mSigNum, &mOldAction, nullptr)); - } - if (mVoidPtr) { - free(mVoidPtr); - } - } - - void DoSomething(); - -public: - void MallocWorker(); - void FreeWorker(); -}; - -TEST_F(SandboxBrokerSigStress, StressTest) -{ - static const int kIters = 6250; - static const int kNsecPerIterPerIter = 4; - struct timespec delay = { 0, 0 }; - pthread_t threads[2]; - - mTestIter = kIters; - - StartThread<SandboxBrokerSigStress, - &SandboxBrokerSigStress::MallocWorker>(&threads[0]); - StartThread<SandboxBrokerSigStress, - &SandboxBrokerSigStress::FreeWorker>(&threads[1]); - - for (int i = kIters; i > 0; --i) { - SignalThread(threads[i % 2]); - while (sem_wait(&mSemaphore) == -1 && errno == EINTR) - /* retry */; - ASSERT_EQ(i - 1, mTestIter); - delay.tv_nsec += kNsecPerIterPerIter; - struct timespec req = delay, rem; - while (nanosleep(&req, &rem) == -1 && errno == EINTR) { - req = rem; - } - } - void *retval; - ASSERT_EQ(0, pthread_join(threads[0], &retval)); - ASSERT_EQ(nullptr, retval); - ASSERT_EQ(0, pthread_join(threads[1], &retval)); - ASSERT_EQ(nullptr, retval); -} - -void -SandboxBrokerSigStress::MallocWorker() -{ - static const size_t kSize = 64; - - void* mem = malloc(kSize); - while (mTestIter > 0) { - ASSERT_NE(mem, mVoidPtr); - mem = mVoidPtr.exchange(mem); - if (mem) { - sched_yield(); - } else { - mem = malloc(kSize); - } - } - if (mem) { - free(mem); - } -} - -void -SandboxBrokerSigStress::FreeWorker() -{ - void *mem = nullptr; - while (mTestIter > 0) { - mem = mVoidPtr.exchange(mem); - if (mem) { - free(mem); - mem = nullptr; - } else { - sched_yield(); - } - } -} - -void -SandboxBrokerSigStress::DoSomething() -{ - int fd; - char c; - struct stat st; - - //fprintf(stderr, "Don't try this at home: %d\n", static_cast<int>(mTestIter)); - switch (mTestIter % 5) { - case 0: - fd = Open("/dev/null", O_RDWR); - ASSERT_GE(fd, 0); - ASSERT_EQ(0, read(fd, &c, 1)); - close(fd); - break; - case 1: - fd = Open("/dev/zero", O_RDONLY); - ASSERT_GE(fd, 0); - ASSERT_EQ(1, read(fd, &c, 1)); - ASSERT_EQ('\0', c); - close(fd); - break; - case 2: - ASSERT_EQ(0, Access("/dev/null", W_OK)); - break; - case 3: - ASSERT_EQ(0, Stat("/proc/self", &st)); - ASSERT_TRUE(S_ISDIR(st.st_mode)); - break; - case 4: - ASSERT_EQ(0, LStat("/proc/self", &st)); - ASSERT_TRUE(S_ISLNK(st.st_mode)); - break; - } - mTestIter--; - sem_post(&mSemaphore); -} -#endif - -} // namespace mozilla diff --git a/security/sandbox/linux/gtest/TestBrokerPolicy.cpp b/security/sandbox/linux/gtest/TestBrokerPolicy.cpp deleted file mode 100644 index 474458446..000000000 --- a/security/sandbox/linux/gtest/TestBrokerPolicy.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* -*- 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 "gtest/gtest.h" - -#include "broker/SandboxBroker.h" - -namespace mozilla { - -static const int MAY_ACCESS = SandboxBroker::MAY_ACCESS; -static const int MAY_READ = SandboxBroker::MAY_READ; -static const int MAY_WRITE = SandboxBroker::MAY_WRITE; -//static const int MAY_CREATE = SandboxBroker::MAY_CREATE; -//static const int RECURSIVE = SandboxBroker::RECURSIVE; -static const auto AddAlways = SandboxBroker::Policy::AddAlways; - -TEST(SandboxBrokerPolicyLookup, Simple) -{ - SandboxBroker::Policy p; - p.AddPath(MAY_READ, "/dev/urandom", AddAlways); - - EXPECT_NE(0, p.Lookup("/dev/urandom")) << "Added path not found."; - EXPECT_EQ(MAY_ACCESS | MAY_READ, p.Lookup("/dev/urandom")) - << "Added path found with wrong perms."; - EXPECT_EQ(0, p.Lookup("/etc/passwd")) << "Non-added path was found."; -} - -TEST(SandboxBrokerPolicyLookup, CopyCtor) -{ - SandboxBroker::Policy psrc; - psrc.AddPath(MAY_READ | MAY_WRITE, "/dev/null", AddAlways); - SandboxBroker::Policy pdst(psrc); - psrc.AddPath(MAY_READ, "/dev/zero", AddAlways); - pdst.AddPath(MAY_READ, "/dev/urandom", AddAlways); - - EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, psrc.Lookup("/dev/null")) - << "Common path absent in copy source."; - EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, pdst.Lookup("/dev/null")) - << "Common path absent in copy destination."; - - EXPECT_EQ(MAY_ACCESS | MAY_READ, psrc.Lookup("/dev/zero")) - << "Source-only path is absent."; - EXPECT_EQ(0, pdst.Lookup("/dev/zero")) - << "Source-only path is present in copy destination."; - - EXPECT_EQ(0, psrc.Lookup("/dev/urandom")) - << "Destination-only path is present in copy source."; - EXPECT_EQ(MAY_ACCESS | MAY_READ, pdst.Lookup("/dev/urandom")) - << "Destination-only path is absent."; - - EXPECT_EQ(0, psrc.Lookup("/etc/passwd")) - << "Non-added path is present in copy source."; - EXPECT_EQ(0, pdst.Lookup("/etc/passwd")) - << "Non-added path is present in copy source."; -} - -TEST(SandboxBrokerPolicyLookup, Recursive) -{ - SandboxBroker::Policy psrc; - psrc.AddPath(MAY_READ | MAY_WRITE, "/dev/null", AddAlways); - psrc.AddPath(MAY_READ, "/dev/zero", AddAlways); - psrc.AddPath(MAY_READ, "/dev/urandom", AddAlways); - - EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, psrc.Lookup("/dev/null")) - << "Basic path is present."; - EXPECT_EQ(MAY_ACCESS | MAY_READ, psrc.Lookup("/dev/zero")) - << "Basic path has no extra flags"; - - psrc.AddDir(MAY_READ | MAY_WRITE, "/dev/"); - - EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, psrc.Lookup("/dev/random")) - << "Permission via recursive dir."; - EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, psrc.Lookup("/dev/sd/0")) - << "Permission via recursive dir, nested deeper"; - EXPECT_EQ(0, psrc.Lookup("/dev/sd/0/")) - << "Invalid path format."; - EXPECT_EQ(0, psrc.Lookup("/usr/dev/sd")) - << "Match must be a prefix."; - - psrc.AddDir(MAY_READ, "/dev/sd/"); - EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, psrc.Lookup("/dev/sd/0")) - << "Extra permissions from parent path granted."; - EXPECT_EQ(0, psrc.Lookup("/dev/..")) - << "Refuse attempted subdir escape."; - - psrc.AddDir(MAY_READ, "/tmp"); - EXPECT_EQ(MAY_ACCESS | MAY_READ, psrc.Lookup("/tmp/good/a")) - << "Check whether dir add with no trailing / was sucessful."; - EXPECT_EQ(0, psrc.Lookup("/tmp_good_but_bad")) - << "Enforce terminator on directories."; - EXPECT_EQ(0, psrc.Lookup("/tmp/.")) - << "Do not allow opening a directory handle."; -} - -} // namespace mozilla diff --git a/security/sandbox/linux/gtest/TestSandboxUtil.cpp b/security/sandbox/linux/gtest/TestSandboxUtil.cpp deleted file mode 100644 index cd29813f9..000000000 --- a/security/sandbox/linux/gtest/TestSandboxUtil.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- 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 "gtest/gtest.h" - -#include "SandboxUtil.h" -#include "SandboxInfo.h" - -#include <pthread.h> - -namespace mozilla { - -// In order to test IsSingleThreaded when the test-running process is -// single-threaded, before assorted XPCOM components have created many -// additional threads, a static initializer is used. - -namespace { - -struct EarlyTest { - bool mWasSingleThreaded; - - EarlyTest() - : mWasSingleThreaded(IsSingleThreaded()) - { } -}; - -static const EarlyTest gEarlyTest; - -} // namespace - -TEST(SandboxUtil, IsSingleThreaded) -{ - // If the test system if affected by kUnexpectedThreads, (1) there's - // no point in doing this test, and (2) if that happens on Mozilla - // CI then burning the tree is an appropriate response. - ASSERT_FALSE(SandboxInfo::Get().Test(SandboxInfo::kUnexpectedThreads)); - EXPECT_TRUE(gEarlyTest.mWasSingleThreaded); - EXPECT_FALSE(IsSingleThreaded()); -} - -} // namespace mozilla diff --git a/security/sandbox/linux/gtest/moz.build b/security/sandbox/linux/gtest/moz.build deleted file mode 100644 index 7aecc7fe3..000000000 --- a/security/sandbox/linux/gtest/moz.build +++ /dev/null @@ -1,27 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -Library('sandboxtest') - -SOURCES = [ - '../SandboxBrokerClient.cpp', - '../SandboxUtil.cpp', - 'TestBroker.cpp', - 'TestBrokerPolicy.cpp', - 'TestSandboxUtil.cpp', -] - -LOCAL_INCLUDES += [ - '/security/sandbox/linux', -] - -include('/ipc/chromium/chromium-config.mozbuild') - -LOCAL_INCLUDES += [ - '/security/sandbox/chromium', -] - -FINAL_LIBRARY = 'xul-gtest' diff --git a/security/sandbox/linux/moz.build b/security/sandbox/linux/moz.build deleted file mode 100644 index 4273da955..000000000 --- a/security/sandbox/linux/moz.build +++ /dev/null @@ -1,116 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -SharedLibrary('mozsandbox') - -# Depend on mozglue if and only if it's a shared library; -# this needs to match mozglue/build/moz.build: -if CONFIG['OS_TARGET'] == 'Android': - USE_LIBS += [ - 'mozglue', - ] - -EXPORTS.mozilla += [ - 'Sandbox.h', - 'SandboxInfo.h', -] - -SOURCES += [ - '../chromium-shim/base/logging.cpp', - '../chromium/base/at_exit.cc', - '../chromium/base/callback_internal.cc', - '../chromium/base/lazy_instance.cc', - '../chromium/base/memory/ref_counted.cc', - '../chromium/base/memory/singleton.cc', - '../chromium/base/strings/safe_sprintf.cc', - '../chromium/base/strings/string16.cc', - '../chromium/base/strings/string_piece.cc', - '../chromium/base/strings/string_util.cc', - '../chromium/base/strings/string_util_constants.cc', - '../chromium/base/strings/stringprintf.cc', - '../chromium/base/strings/utf_string_conversion_utils.cc', - '../chromium/base/strings/utf_string_conversions.cc', - '../chromium/base/synchronization/condition_variable_posix.cc', - '../chromium/base/synchronization/lock.cc', - '../chromium/base/synchronization/lock_impl_posix.cc', - '../chromium/base/synchronization/waitable_event_posix.cc', - '../chromium/base/third_party/icu/icu_utf.cc', - '../chromium/base/threading/platform_thread_internal_posix.cc', - '../chromium/base/threading/platform_thread_linux.cc', - '../chromium/base/threading/platform_thread_posix.cc', - '../chromium/base/threading/thread_collision_warner.cc', - '../chromium/base/threading/thread_id_name_manager.cc', - '../chromium/base/threading/thread_local_posix.cc', - '../chromium/base/threading/thread_restrictions.cc', - '../chromium/base/time/time.cc', - '../chromium/base/time/time_posix.cc', - '../chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc', - '../chromium/sandbox/linux/bpf_dsl/codegen.cc', - '../chromium/sandbox/linux/bpf_dsl/dump_bpf.cc', - '../chromium/sandbox/linux/bpf_dsl/policy.cc', - '../chromium/sandbox/linux/bpf_dsl/policy_compiler.cc', - '../chromium/sandbox/linux/bpf_dsl/syscall_set.cc', - '../chromium/sandbox/linux/seccomp-bpf/die.cc', - '../chromium/sandbox/linux/seccomp-bpf/syscall.cc', - '../chromium/sandbox/linux/seccomp-bpf/trap.cc', - '../chromium/sandbox/linux/services/syscall_wrappers.cc', - 'broker/SandboxBrokerCommon.cpp', - 'LinuxCapabilities.cpp', - 'Sandbox.cpp', - 'SandboxBrokerClient.cpp', - 'SandboxChroot.cpp', - 'SandboxFilter.cpp', - 'SandboxFilterUtil.cpp', - 'SandboxHooks.cpp', - 'SandboxInfo.cpp', - 'SandboxLogging.cpp', - 'SandboxUtil.cpp', -] - -# This copy of SafeSPrintf doesn't need to avoid the Chromium logging -# dependency like the one in libxul does, but this way the behavior is -# consistent. See also the comment in SandboxLogging.h. -SOURCES['../chromium/base/strings/safe_sprintf.cc'].flags += ['-DNDEBUG'] - -# Keep clang and GCC from warning about intentional 'switch' fallthrough in icu_utf.cc: -if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']: - SOURCES['../chromium/base/third_party/icu/icu_utf.cc'].flags += ['-Wno-implicit-fallthrough'] - -if CONFIG['GNU_CXX']: - CXXFLAGS += ['-Wno-shadow'] - SOURCES['../chromium/sandbox/linux/services/syscall_wrappers.cc'].flags += [ - '-Wno-empty-body', - ] - -# gcc lto likes to put the top level asm in syscall.cc in a different partition -# from the function using it which breaks the build. Work around that by -# forcing there to be only one partition. -if '-flto' in CONFIG['OS_CXXFLAGS'] and not CONFIG['CLANG_CXX']: - LDFLAGS += ['--param lto-partitions=1'] - -DEFINES['NS_NO_XPCOM'] = True -DISABLE_STL_WRAPPING = True - -LOCAL_INCLUDES += ['/security/sandbox/linux'] -LOCAL_INCLUDES += ['/security/sandbox/chromium-shim'] -LOCAL_INCLUDES += ['/security/sandbox/chromium'] -LOCAL_INCLUDES += ['/nsprpub'] - - -if CONFIG['OS_TARGET'] != 'Android': - # Needed for clock_gettime with glibc < 2.17: - OS_LIBS += [ - 'rt', - ] - -DIRS += [ - 'broker', - 'glue', -] - -TEST_DIRS += [ - 'gtest', -] |