Commit 660e2d46 authored by rickyz's avatar rickyz Committed by Commit bot

Allow using the namespace sandbox in zygote host.

Currently, this is gated behind the enable-namespace-sandbox switch.
Furthermore, the namespace sandbox is only used if seccomp-bpf is
supported.

BUG=312380

Review URL: https://codereview.chromium.org/897723005

Cr-Commit-Position: refs/heads/master@{#315177}
parent 0264cb11
......@@ -2,6 +2,7 @@ gconf-service
libasound2 (>= 1.0.23)
libc6 (>= 2.11)
libcairo2 (>= 1.6.0)
libcap2 (>= 2.10)
libcups2 (>= 1.4.0)
libdbus-1-3 (>= 1.2.14)
libexpat1 (>= 1.95.8)
......
......@@ -2,6 +2,7 @@ gconf-service
libasound2 (>= 1.0.23)
libc6 (>= 2.11)
libcairo2 (>= 1.6.0)
libcap2 (>= 2.10)
libcups2 (>= 1.4.0)
libdbus-1-3 (>= 1.2.14)
libexpat1 (>= 1.95.8)
......
......@@ -3,6 +3,7 @@ ld-linux.so.2(GLIBC_2.1)
ld-linux.so.2(GLIBC_2.3)
libasound.so.2
libcairo.so.2
libcap.so.2
libc.so.6
libc.so.6(GLIBC_2.0)
libc.so.6(GLIBC_2.1)
......
......@@ -3,6 +3,7 @@ ld-linux-x86-64.so.2(GLIBC_2.2.5)(64bit)
ld-linux-x86-64.so.2(GLIBC_2.3)(64bit)
libasound.so.2()(64bit)
libcairo.so.2()(64bit)
libcap.so.2()(64bit)
libc.so.6()(64bit)
libc.so.6(GLIBC_2.11)(64bit)
libc.so.6(GLIBC_2.2.5)(64bit)
......
......@@ -147,6 +147,7 @@
'dependencies': [
# Required by nacl_fork_delegate_linux.cc.
'../sandbox/sandbox.gyp:suid_sandbox_client',
'../sandbox/sandbox.gyp:sandbox_services',
]
}],
],
......
......@@ -25,6 +25,8 @@
#include "components/nacl/loader/sandbox_linux/nacl_bpf_sandbox_linux.h"
#include "content/public/common/content_switches.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/services/credentials.h"
#include "sandbox/linux/services/namespace_sandbox.h"
#include "sandbox/linux/services/proc_util.h"
#include "sandbox/linux/services/thread_helpers.h"
#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
......@@ -111,25 +113,34 @@ void NaClSandbox::InitializeLayerOneSandbox() {
CHECK(MaybeSetProcessNonDumpable());
CHECK(IsSandboxed());
layer_one_enabled_ = true;
} else if (sandbox::NamespaceSandbox::InNewUserNamespace()) {
CHECK(sandbox::Credentials::MoveToNewUserNS());
CHECK(sandbox::Credentials::DropFileSystemAccess());
CHECK(sandbox::Credentials::DropAllCapabilities());
CHECK(IsSandboxed());
layer_one_enabled_ = true;
}
}
void NaClSandbox::CheckForExpectedNumberOfOpenFds() {
// We expect to have the following FDs open:
// 1-3) stdin, stdout, stderr.
// 4) The /dev/urandom FD used by base::GetUrandomFD().
// 5) A dummy pipe FD used to overwrite kSandboxIPCChannel.
// 6) The socket for the Chrome IPC channel that's connected to the
// browser process, kPrimaryIPCChannel.
// We also have an fd for /proc (proc_fd_), but CountOpenFds excludes this.
//
// This sanity check ensures that dynamically loaded libraries don't
// leave any FDs open before we enable the sandbox.
int expected_num_fds = 6;
if (setuid_sandbox_client_->IsSuidSandboxChild()) {
// We expect to have the following FDs open:
// 1-3) stdin, stdout, stderr.
// 4) The /dev/urandom FD used by base::GetUrandomFD().
// 5) A dummy pipe FD used to overwrite kSandboxIPCChannel.
// 6) The socket created by the SUID sandbox helper, used by ChrootMe().
// After ChrootMe(), this is no longer connected to anything.
// (Only present when running under the SUID sandbox.)
// 7) The socket for the Chrome IPC channel that's connected to the
// browser process, kPrimaryIPCChannel.
//
// This sanity check ensures that dynamically loaded libraries don't
// leave any FDs open before we enable the sandbox.
CHECK_EQ(7, sandbox::ProcUtil::CountOpenFds(proc_fd_.get()));
// When using the setuid sandbox, there is one additional socket used for
// ChrootMe(). After ChrootMe(), it is no longer connected to anything.
++expected_num_fds;
}
CHECK_EQ(expected_num_fds, sandbox::ProcUtil::CountOpenFds(proc_fd_.get()));
}
void NaClSandbox::InitializeLayerTwoSandbox(bool uses_nonsfi_mode) {
......
......@@ -35,6 +35,8 @@
#include "components/nacl/loader/nacl_helper_linux.h"
#include "content/public/common/content_descriptors.h"
#include "content/public/common/content_switches.h"
#include "sandbox/linux/services/namespace_sandbox.h"
#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
#include "sandbox/linux/suid/client/setuid_sandbox_host.h"
#include "sandbox/linux/suid/common/sandbox.h"
......@@ -146,11 +148,23 @@ void NaClForkDelegate::Init(const int sandboxdesc,
return;
}
// TODO(rickyz): Make IsSuidSandboxChild a static function.
scoped_ptr<sandbox::SetuidSandboxClient> setuid_sandbox_client(
sandbox::SetuidSandboxClient::Create());
const bool using_setuid_sandbox = setuid_sandbox_client->IsSuidSandboxChild();
const bool using_namespace_sandbox =
sandbox::NamespaceSandbox::InNewUserNamespace();
CHECK(!(using_setuid_sandbox && using_namespace_sandbox));
if (enable_layer1_sandbox) {
CHECK(using_setuid_sandbox || using_namespace_sandbox);
}
scoped_ptr<sandbox::SetuidSandboxHost> setuid_sandbox_host(
sandbox::SetuidSandboxHost::Create());
// For communications between the NaCl loader process and
// the SUID sandbox.
// the browser process.
int nacl_sandbox_descriptor =
base::GlobalDescriptors::kBaseDescriptor + kSandboxIPCChannel;
// Confirm a hard-wired assumption.
......@@ -240,7 +254,7 @@ void NaClForkDelegate::Init(const int sandboxdesc,
base::LaunchOptions options;
base::ScopedFD dummy_fd;
if (enable_layer1_sandbox) {
if (using_setuid_sandbox) {
// NaCl needs to keep tight control of the cmd_line, so prepend the
// setuid sandbox wrapper manually.
base::FilePath sandbox_path = setuid_sandbox_host->GetSandboxBinaryPath();
......@@ -266,11 +280,16 @@ void NaClForkDelegate::Init(const int sandboxdesc,
options.clear_environ = true;
AddPassthroughEnvToOptions(&options);
if (!base::LaunchProcess(argv_to_launch, options).IsValid())
base::Process process =
using_namespace_sandbox
? sandbox::NamespaceSandbox::LaunchProcess(argv_to_launch, options)
: base::LaunchProcess(argv_to_launch, options);
if (!process.IsValid())
status_ = kNaClHelperLaunchFailed;
// parent and error cases are handled below
if (enable_layer1_sandbox) {
if (using_setuid_sandbox) {
// Sanity check that dummy_fd was kept alive for LaunchProcess.
DCHECK(dummy_fd.is_valid());
}
......
......@@ -38,6 +38,9 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "sandbox/linux/services/credentials.h"
#include "sandbox/linux/services/namespace_sandbox.h"
#include "sandbox/linux/services/namespace_utils.h"
#include "sandbox/linux/suid/client/setuid_sandbox_host.h"
#include "sandbox/linux/suid/common/sandbox.h"
#include "ui/base/ui_base_switches.h"
......@@ -49,12 +52,14 @@
namespace content {
namespace {
// Receive a fixed message on fd and return the sender's PID.
// Returns true if the message received matches the expected message.
static bool ReceiveFixedMessage(int fd,
const char* expect_msg,
size_t expect_len,
base::ProcessId* sender_pid) {
bool ReceiveFixedMessage(int fd,
const char* expect_msg,
size_t expect_len,
base::ProcessId* sender_pid) {
char buf[expect_len + 1];
ScopedVector<base::ScopedFD> fds_vec;
......@@ -69,6 +74,8 @@ static bool ReceiveFixedMessage(int fd,
return true;
}
} // namespace
// static
ZygoteHost* ZygoteHost::GetInstance() {
return ZygoteHostImpl::GetInstance();
......@@ -79,7 +86,7 @@ ZygoteHostImpl::ZygoteHostImpl()
control_lock_(),
pid_(-1),
init_(false),
using_suid_sandbox_(false),
use_suid_sandbox_for_adj_oom_score_(false),
sandbox_binary_(),
have_read_sandbox_status_word_(false),
sandbox_status_(0),
......@@ -141,8 +148,16 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) {
sandbox_binary_ = sandbox_cmd.c_str();
const bool using_namespace_sandbox = ShouldUseNamespaceSandbox();
// A non empty sandbox_cmd means we want a SUID sandbox.
using_suid_sandbox_ = !sandbox_cmd.empty();
const bool using_suid_sandbox =
!sandbox_cmd.empty() && !using_namespace_sandbox;
// Use the SUID sandbox for adjusting OOM scores when we are using the setuid
// or namespace sandbox. This is needed beacuse the processes are
// non-dumpable, so /proc/pid/oom_score_adj can only be written by root.
use_suid_sandbox_for_adj_oom_score_ =
using_namespace_sandbox || using_suid_sandbox;
// Start up the sandbox host process and get the file descriptor for the
// renderers to talk to it.
......@@ -150,7 +165,7 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) {
fds_to_map.push_back(std::make_pair(sfd, GetSandboxFD()));
base::ScopedFD dummy_fd;
if (using_suid_sandbox_) {
if (using_suid_sandbox) {
scoped_ptr<sandbox::SetuidSandboxHost> sandbox_host(
sandbox::SetuidSandboxHost::Create());
sandbox_host->PrependWrapper(&cmd_line);
......@@ -159,11 +174,15 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) {
}
options.fds_to_remap = &fds_to_map;
base::Process process = base::LaunchProcess(cmd_line.argv(), options);
base::Process process =
using_namespace_sandbox
? sandbox::NamespaceSandbox::LaunchProcess(cmd_line, options)
: base::LaunchProcess(cmd_line, options);
CHECK(process.IsValid()) << "Failed to launch zygote process";
dummy_fd.reset();
if (using_suid_sandbox_) {
if (using_suid_sandbox) {
// The SUID sandbox will execute the zygote in a new PID namespace, and
// the main zygote process will then fork from there. Watch now our
// elaborate dance to find and validate the zygote's PID.
......@@ -458,7 +477,7 @@ void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid,
selinux_valid = true;
}
if (using_suid_sandbox_ && !selinux) {
if (use_suid_sandbox_for_adj_oom_score_ && !selinux) {
#if defined(USE_TCMALLOC)
// If heap profiling is running, these processes are not exiting, at least
// on ChromeOS. The easiest thing to do is not launch them when profiling.
......@@ -482,7 +501,7 @@ void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid,
base::LaunchProcess(adj_oom_score_cmdline, options);
if (sandbox_helper_process.IsValid())
base::EnsureProcessGetsReaped(sandbox_helper_process.Pid());
} else if (!using_suid_sandbox_) {
} else if (!use_suid_sandbox_for_adj_oom_score_) {
if (!base::AdjustOOMScore(pid, score))
PLOG(ERROR) << "Failed to adjust OOM score of renderer with pid " << pid;
}
......@@ -559,4 +578,22 @@ int ZygoteHostImpl::GetSandboxStatus() const {
return 0;
}
bool ZygoteHostImpl::ShouldUseNamespaceSandbox() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kNoSandbox)) {
return false;
}
if (!command_line.HasSwitch(switches::kEnableNamespaceSandbox)) {
return false;
}
if (!sandbox::Credentials::CanCreateProcessInNewUserNS()) {
return false;
}
return true;
}
} // namespace content
......@@ -82,6 +82,9 @@ class CONTENT_EXPORT ZygoteHostImpl : public ZygoteHost {
ssize_t ReadReply(void* buf, size_t buflen);
// Whether we should use the namespace sandbox instead of the setuid sandbox.
bool ShouldUseNamespaceSandbox();
int control_fd_; // the socket to the zygote
// A lock protecting all communication with the zygote. This lock must be
// acquired before sending a command and released after the result has been
......@@ -89,7 +92,7 @@ class CONTENT_EXPORT ZygoteHostImpl : public ZygoteHost {
base::Lock control_lock_;
pid_t pid_;
bool init_;
bool using_suid_sandbox_;
bool use_suid_sandbox_for_adj_oom_score_;
std::string sandbox_binary_;
bool have_read_sandbox_status_word_;
int sandbox_status_;
......
......@@ -357,6 +357,9 @@ const char kEnableLogging[] = "enable-logging";
// Enables the memory benchmarking extension
const char kEnableMemoryBenchmarking[] = "enable-memory-benchmarking";
// Prefer the namespace sandbox over the setuid sandbox when possible.
const char kEnableNamespaceSandbox[] = "enable-namespace-sandbox";
// Enables the network information API.
const char kEnableNetworkInformation[] = "enable-network-information";
......
......@@ -110,6 +110,7 @@ CONTENT_EXPORT extern const char kEnableImageColorProfiles[];
CONTENT_EXPORT extern const char kEnableLCDText[];
CONTENT_EXPORT extern const char kEnableLogging[];
extern const char kEnableMemoryBenchmarking[];
CONTENT_EXPORT extern const char kEnableNamespaceSandbox[];
CONTENT_EXPORT extern const char kEnableNetworkInformation[];
CONTENT_EXPORT extern const char kEnableOneCopy[];
CONTENT_EXPORT extern const char kEnableOverlayFullscreenVideo[];
......
......@@ -39,8 +39,10 @@
#include "content/public/common/zygote_fork_delegate_linux.h"
#include "content/zygote/zygote_linux.h"
#include "crypto/nss_util.h"
#include "sandbox/linux/services/credentials.h"
#include "sandbox/linux/services/init_process_reaper.h"
#include "sandbox/linux/services/libc_urandom_override.h"
#include "sandbox/linux/services/namespace_sandbox.h"
#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
#include "third_party/skia/include/ports/SkFontConfigInterface.h"
......@@ -399,6 +401,24 @@ static bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback) {
return true;
}
static bool MaybeSetProcessNonDumpable() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kAllowSandboxDebugging)) {
// If sandbox debugging is allowed, install a handler for sandbox-related
// crash testing.
InstallSandboxCrashTestHandler();
return true;
}
if (prctl(PR_SET_DUMPABLE, 0) != 0) {
PLOG(ERROR) << "Failed to set non-dumpable flag";
return false;
}
return prctl(PR_GET_DUMPABLE) == 0;
}
// Enter the setuid sandbox. This requires the current process to have been
// created through the setuid sandbox.
static bool EnterSuidSandbox(sandbox::SetuidSandboxClient* setuid_sandbox,
......@@ -433,41 +453,27 @@ static bool EnterSuidSandbox(sandbox::SetuidSandboxClient* setuid_sandbox,
CHECK(CreateInitProcessReaper(post_fork_parent_callback));
}
#if !defined(OS_OPENBSD)
// Previously, we required that the binary be non-readable. This causes the
// kernel to mark the process as non-dumpable at startup. The thinking was
// that, although we were putting the renderers into a PID namespace (with
// the SUID sandbox), they would nonetheless be in the /same/ PID
// namespace. So they could ptrace each other unless they were non-dumpable.
//
// If the binary was readable, then there would be a window between process
// startup and the point where we set the non-dumpable flag in which a
// compromised renderer could ptrace attach.
//
// However, now that we have a zygote model, only the (trusted) zygote
// exists at this point and we can set the non-dumpable flag which is
// inherited by all our renderer children.
//
// Note: a non-dumpable process can't be debugged. To debug sandbox-related
// issues, one can specify --allow-sandbox-debugging to let the process be
// dumpable.
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (!command_line.HasSwitch(switches::kAllowSandboxDebugging)) {
prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
LOG(ERROR) << "Failed to set non-dumpable flag";
return false;
}
} else {
// If sandbox debugging is allowed, install a handler for sandbox-related
// crash testing.
InstallSandboxCrashTestHandler();
CHECK(MaybeSetProcessNonDumpable());
return true;
}
static void EnterNamespaceSandbox(base::Closure* post_fork_parent_callback) {
pid_t pid = getpid();
if (sandbox::NamespaceSandbox::InNewPidNamespace()) {
CHECK_EQ(1, pid);
}
#endif
CHECK(sandbox::Credentials::MoveToNewUserNS());
CHECK(sandbox::Credentials::DropFileSystemAccess());
CHECK(sandbox::Credentials::DropAllCapabilities());
return true;
// This needs to happen after moving to a new user NS, since doing so involves
// writing the UID/GID map.
CHECK(MaybeSetProcessNonDumpable());
if (pid == 1) {
CHECK(CreateInitProcessReaper(post_fork_parent_callback));
}
}
#if defined(ADDRESS_SANITIZER)
......@@ -526,10 +532,8 @@ static pid_t ForkSanitizerCoverageHelper(
#endif // defined(ADDRESS_SANITIZER)
// If |is_suid_sandbox_child|, then make sure that the setuid sandbox is
// engaged.
static void EnterLayerOneSandbox(LinuxSandbox* linux_sandbox,
bool is_suid_sandbox_child,
const bool using_layer1_sandbox,
base::Closure* post_fork_parent_callback) {
DCHECK(linux_sandbox);
......@@ -542,10 +546,13 @@ static void EnterLayerOneSandbox(LinuxSandbox* linux_sandbox,
sandbox::SetuidSandboxClient* setuid_sandbox =
linux_sandbox->setuid_sandbox_client();
if (is_suid_sandbox_child) {
if (setuid_sandbox->IsSuidSandboxChild()) {
CHECK(EnterSuidSandbox(setuid_sandbox, post_fork_parent_callback))
<< "Failed to enter setuid sandbox";
} else if (sandbox::NamespaceSandbox::InNewUserNamespace()) {
EnterNamespaceSandbox(post_fork_parent_callback);
} else {
CHECK(!using_layer1_sandbox);
}
}
......@@ -583,9 +590,12 @@ bool ZygoteMain(const MainFunctionParams& params,
linux_sandbox->PreinitializeSandbox();
}
const bool must_enable_setuid_sandbox =
const bool using_setuid_sandbox =
linux_sandbox->setuid_sandbox_client()->IsSuidSandboxChild();
if (must_enable_setuid_sandbox) {
const bool using_namespace_sandbox =
sandbox::NamespaceSandbox::InNewUserNamespace();
if (using_setuid_sandbox) {
linux_sandbox->setuid_sandbox_client()->CloseDummyFile();
// Let the ZygoteHost know we're booting up.
......@@ -597,10 +607,10 @@ bool ZygoteMain(const MainFunctionParams& params,
VLOG(1) << "ZygoteMain: initializing " << fork_delegates.size()
<< " fork delegates";
for (ScopedVector<ZygoteForkDelegate>::iterator i = fork_delegates.begin();
i != fork_delegates.end();
++i) {
(*i)->Init(GetSandboxFD(), must_enable_setuid_sandbox);
const bool using_layer1_sandbox =
using_setuid_sandbox || using_namespace_sandbox;
for (ZygoteForkDelegate* fork_delegate : fork_delegates) {
fork_delegate->Init(GetSandboxFD(), using_layer1_sandbox);
}
const std::vector<int> sandbox_fds_to_close_post_fork =
......@@ -613,7 +623,7 @@ bool ZygoteMain(const MainFunctionParams& params,
base::Bind(&CloseFds, fds_to_close_post_fork);
// Turn on the first layer of the sandbox if the configuration warrants it.
EnterLayerOneSandbox(linux_sandbox, must_enable_setuid_sandbox,
EnterLayerOneSandbox(linux_sandbox, using_layer1_sandbox,
&post_fork_parent_callback);
// Extra children and file descriptors created that the Zygote must have
......@@ -638,7 +648,7 @@ bool ZygoteMain(const MainFunctionParams& params,
int sandbox_flags = linux_sandbox->GetStatus();
bool setuid_sandbox_engaged = sandbox_flags & kSandboxLinuxSUID;
CHECK_EQ(must_enable_setuid_sandbox, setuid_sandbox_engaged);
CHECK_EQ(using_setuid_sandbox, setuid_sandbox_engaged);
Zygote zygote(sandbox_flags, fork_delegates.Pass(), extra_children,
extra_fds);
......
......@@ -11,6 +11,7 @@
#include <string>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/environment.h"
......@@ -59,6 +60,13 @@ const char kSandboxNETNSEnvironmentVarName[] = "SBX_NET_NS";
base::Process NamespaceSandbox::LaunchProcess(
const base::CommandLine& cmdline,
const base::LaunchOptions& options) {
return LaunchProcess(cmdline.argv(), options);
}
// static
base::Process NamespaceSandbox::LaunchProcess(
const std::vector<std::string>& argv,
const base::LaunchOptions& options) {
int clone_flags = 0;
int ns_types[] = {CLONE_NEWUSER, CLONE_NEWPID, CLONE_NEWNET};
for (const int ns_type : ns_types) {
......@@ -91,7 +99,7 @@ base::Process NamespaceSandbox::LaunchProcess(
SetEnvironForNamespaceType(environ, environ_name, clone_flags & flag);
}
return base::LaunchProcess(cmdline, launch_options);
return base::LaunchProcess(argv, launch_options);
}
// static
......
......@@ -5,6 +5,9 @@
#ifndef SANDBOX_LINUX_SERVICES_NAMESPACE_SANDBOX_H_
#define SANDBOX_LINUX_SERVICES_NAMESPACE_SANDBOX_H_
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/macros.h"
#include "base/process/launch.h"
......@@ -41,6 +44,8 @@ class SANDBOX_EXPORT NamespaceSandbox {
// overrides them.
static base::Process LaunchProcess(const base::CommandLine& cmdline,
const base::LaunchOptions& options);
static base::Process LaunchProcess(const std::vector<std::string>& argv,
const base::LaunchOptions& options);
// Returns whether the namespace sandbox created a new user, PID, and network
// namespace. In particular, InNewUserNamespace should return true iff the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment