Skip to content

Commit

Permalink
Bug 1151607 - Step 2: Apply net/ipc namespace separation and chroot t…
Browse files Browse the repository at this point in the history
…o media plugins. r=kang

This needs more unit tests for the various pieces of what's going on
here (LinuxCapabilities, SandboxChroot, UnshareUserNamespace()) but
that's nontrivial due to needing a single-threaded process -- and
currently they can't be run on Mozilla's CI anyway due to needing user
namespaces, and local testing can just try using GMP and manually
inspecting the child process.  So that will be a followup.
  • Loading branch information
rmottola committed Aug 3, 2019
1 parent 9448057 commit 3dc0e57
Show file tree
Hide file tree
Showing 9 changed files with 580 additions and 0 deletions.
28 changes: 28 additions & 0 deletions security/sandbox/linux/LinuxCapabilities.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* -*- 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
119 changes: 119 additions & 0 deletions security/sandbox/linux/LinuxCapabilities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/* -*- 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:
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;
}
}

// 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
88 changes: 88 additions & 0 deletions security/sandbox/linux/Sandbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "Sandbox.h"

#include "LinuxCapabilities.h"
#include "LinuxSched.h"
#include "SandboxChroot.h"
#include "SandboxFilter.h"
#include "SandboxInternal.h"
#include "SandboxLogging.h"
Expand All @@ -27,11 +31,13 @@

#include "mozilla/Atomics.h"
#include "mozilla/SandboxInfo.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/unused.h"
#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
#if defined(ANDROID)
#include "sandbox/linux/services/android_ucontext.h"
#endif
#include "sandbox/linux/services/linux_syscalls.h"

#ifdef MOZ_ASAN
// Copy libsanitizer declarations to avoid depending on ASAN headers.
Expand Down Expand Up @@ -67,6 +73,8 @@ static int gMediaPluginFileDesc = -1;
static const char *gMediaPluginFilePath;
#endif

static UniquePtr<SandboxChroot> gChrootHelper;

/**
* This is the SIGSYS handler function. It is used to report to the user
* which system call has been denied by Seccomp.
Expand Down Expand Up @@ -285,6 +293,12 @@ BroadcastSetThreadSandbox(SandboxType aType)
SANDBOX_LOG_ERROR("opendir /proc/self/task: %s\n", strerror(errno));
MOZ_CRASH();
}

if (gChrootHelper) {
gChrootHelper->Invoke();
gChrootHelper = nullptr;
}

signum = FindFreeSignalNumber();
if (signum == 0) {
SANDBOX_LOG_ERROR("No available signal numbers!");
Expand Down Expand Up @@ -426,6 +440,80 @@ void
SandboxEarlyInit(GeckoProcessType aType, bool aIsNuwa)
{
MOZ_RELEASE_ASSERT(IsSingleThreaded());

// Which kinds of resource isolation (of those that need to be set
// up at this point) can be used by this process?
bool canChroot = false;
bool canUnshareNet = false;
bool canUnshareIPC = false;

switch (aType) {
case GeckoProcessType_Default:
MOZ_ASSERT(false, "SandboxEarlyInit in parent process");
return;
#ifdef MOZ_GMP_SANDBOX
case GeckoProcessType_GMPlugin:
canUnshareNet = true;
canUnshareIPC = true;
canChroot = true;
break;
#endif
// In the future, content processes will be able to use some of
// these.
default:
// Other cases intentionally left blank.
break;
}

// If there's nothing to do, then we're done.
if (!canChroot && !canUnshareNet && !canUnshareIPC) {
return;
}

// If capabilities can't be gained, then nothing can be done.
const SandboxInfo info = SandboxInfo::Get();
if (!info.Test(SandboxInfo::kHasUserNamespaces)) {
return;
}

// The failure cases for the various unshares, and setting up the
// chroot helper, don't strictly need to be fatal -- but they also
// shouldn't fail on any reasonable system, so let's take the small
// risk of breakage over the small risk of quietly providing less
// security than we expect. (Unlike in SandboxInfo, this is in the
// child process, so crashing here isn't as severe a response to the
// unexpected.)
if (!UnshareUserNamespace()) {
SANDBOX_LOG_ERROR("unshare(CLONE_NEWUSER): %s", strerror(errno));
// If CanCreateUserNamespace (SandboxInfo.cpp) returns true, then
// the unshare shouldn't have failed.
MOZ_CRASH("unshare(CLONE_NEWUSER)");
}
// No early returns after this point! We need to drop the
// capabilities that were gained by unsharing the user namesapce.

if (canUnshareIPC && syscall(__NR_unshare, CLONE_NEWIPC) != 0) {
SANDBOX_LOG_ERROR("unshare(CLONE_NEWIPC): %s", strerror(errno));
MOZ_CRASH("unshare(CLONE_NEWIPC)");
}

if (canUnshareNet && syscall(__NR_unshare, CLONE_NEWNET) != 0) {
SANDBOX_LOG_ERROR("unshare(CLONE_NEWNET): %s", strerror(errno));
MOZ_CRASH("unshare(CLONE_NEWNET)");
}

if (canChroot) {
gChrootHelper = MakeUnique<SandboxChroot>();
if (!gChrootHelper->Prepare()) {
SANDBOX_LOG_ERROR("failed to set up chroot helper");
MOZ_CRASH("SandboxChroot::Prepare");
}
}

if (!LinuxCapabilities().SetCurrent()) {
SANDBOX_LOG_ERROR("dropping capabilities: %s", strerror(errno));
MOZ_CRASH("can't drop capabilities");
}
}

#ifdef MOZ_CONTENT_SANDBOX
Expand Down
Loading

0 comments on commit 3dc0e57

Please sign in to comment.