blob: 640fc872f68b1f26d93d3ff2b9acc69ebe8d8f2c [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include "base/command_line.h"
#include "base/eintr_wrapper.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/time.h"
#include "content/common/sandbox_linux.h"
#include "content/common/seccomp_sandbox.h"
#include "content/common/sandbox_seccomp_bpf_linux.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/sandbox_linux.h"
#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
namespace {
void LogSandboxStarted(const std::string& sandbox_name) {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
const std::string process_type =
command_line.GetSwitchValueASCII(switches::kProcessType);
const std::string activated_sandbox =
"Activated " + sandbox_name + " sandbox for process type: " +
process_type + ".";
#if defined(OS_CHROMEOS)
LOG(WARNING) << activated_sandbox;
#else
VLOG(1) << activated_sandbox;
#endif
}
// Implement the command line enabling logic for seccomp-legacy.
bool IsSeccompLegacyDesired() {
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kNoSandbox)) {
return false;
}
#if defined(SECCOMP_SANDBOX)
#if defined(NDEBUG)
// Off by default. Allow turning on with a switch.
return command_line->HasSwitch(switches::kEnableSeccompSandbox);
#else
// On by default. Allow turning off with a switch.
return !command_line->HasSwitch(switches::kDisableSeccompSandbox);
#endif // NDEBUG
#endif // SECCOMP_SANDBOX
return false;
}
// Our "policy" on whether or not to enable seccomp-legacy. Only renderers are
// supported.
bool ShouldEnableSeccompLegacy(const std::string& process_type) {
if (IsSeccompLegacyDesired() &&
process_type == switches::kRendererProcess) {
return true;
} else {
return false;
}
}
} // namespace
namespace content {
LinuxSandbox::LinuxSandbox()
: proc_fd_(-1),
seccomp_bpf_started_(false),
pre_initialized_(false),
seccomp_legacy_supported_(false),
seccomp_bpf_supported_(false),
setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
if (setuid_sandbox_client_ == NULL) {
LOG(FATAL) << "Failed to instantiate the setuid sandbox client.";
}
}
LinuxSandbox::~LinuxSandbox() {
}
LinuxSandbox* LinuxSandbox::GetInstance() {
LinuxSandbox* instance = Singleton<LinuxSandbox>::get();
CHECK(instance);
return instance;
}
void LinuxSandbox::PreinitializeSandboxBegin() {
CHECK(!pre_initialized_);
seccomp_legacy_supported_ = false;
seccomp_bpf_supported_ = false;
#if defined(SECCOMP_SANDBOX)
if (IsSeccompLegacyDesired()) {
proc_fd_ = open("/proc", O_DIRECTORY | O_RDONLY);
if (proc_fd_ < 0) {
LOG(ERROR) << "Cannot access \"/proc\". Disabling seccomp-legacy "
"sandboxing.";
// Now is a good time to figure out if we can support seccomp sandboxing
// at all. We will call SupportsSeccompSandbox again later, when actually
// enabling it, but we allow the implementation to cache some information.
// This is the only place where we will log full lack of seccomp-legacy
// support.
} else if (!SupportsSeccompSandbox(proc_fd_)) {
VLOG(1) << "Lacking support for seccomp-legacy sandbox.";
CHECK_EQ(HANDLE_EINTR(close(proc_fd_)), 0);
proc_fd_ = -1;
} else {
seccomp_legacy_supported_ = true;
}
}
#endif // SECCOMP_SANDBOX
// Similarly, we "pre-warm" the code that detects supports for seccomp BPF.
// TODO(jln): Use proc_fd_ here too once we're comfortable it does not create
// an additional security risk.
if (SandboxSeccompBpf::IsSeccompBpfDesired()) {
if (!SandboxSeccompBpf::SupportsSandbox()) {
VLOG(1) << "Lacking support for seccomp-bpf sandbox.";
} else {
seccomp_bpf_supported_ = true;
}
}
pre_initialized_ = true;
}
// Once we finally know our process type, we can cleanup proc_fd_
// or pass it to seccomp-legacy.
void LinuxSandbox::PreinitializeSandboxFinish(
const std::string& process_type) {
CHECK(pre_initialized_);
if (proc_fd_ >= 0) {
if (ShouldEnableSeccompLegacy(process_type)) {
#if defined(SECCOMP_SANDBOX)
SeccompSandboxSetProcFd(proc_fd_);
#endif
} else {
DCHECK_GE(proc_fd_, 0);
CHECK_EQ(HANDLE_EINTR(close(proc_fd_)), 0);
}
proc_fd_ = -1;
}
}
void LinuxSandbox::PreinitializeSandbox(const std::string& process_type) {
PreinitializeSandboxBegin();
PreinitializeSandboxFinish(process_type);
}
int LinuxSandbox::GetStatus() const {
CHECK(pre_initialized_);
int sandbox_flags = 0;
if (setuid_sandbox_client_->IsSandboxed()) {
sandbox_flags |= kSandboxLinuxSUID;
if (setuid_sandbox_client_->IsInNewPIDNamespace())
sandbox_flags |= kSandboxLinuxPIDNS;
if (setuid_sandbox_client_->IsInNewNETNamespace())
sandbox_flags |= kSandboxLinuxNetNS;
}
if (seccomp_bpf_supported() &&
SandboxSeccompBpf::ShouldEnableSeccompBpf(switches::kRendererProcess)) {
// We report whether the sandbox will be activated when renderers go
// through sandbox initialization.
sandbox_flags |= kSandboxLinuxSeccompBpf;
}
// We only try to enable seccomp-legacy when seccomp-bpf is not supported
// or not enabled.
if (!(sandbox_flags & kSandboxLinuxSeccompBpf) &&
seccomp_legacy_supported() &&
ShouldEnableSeccompLegacy(switches::kRendererProcess)) {
// Same here, what we report is what we will do for the renderer.
sandbox_flags |= kSandboxLinuxSeccompLegacy;
}
return sandbox_flags;
}
bool LinuxSandbox::IsSingleThreaded() const {
// TODO(jln): re-implement this properly and use our proc_fd_ if available.
// Possibly racy, but it's ok because this is more of a debug check to catch
// new threaded situations arising during development.
int num_threads = file_util::CountFilesCreatedAfter(
FilePath("/proc/self/task"),
base::Time::UnixEpoch());
// We pass the test if we don't know ( == 0), because the setuid sandbox
// will prevent /proc access in some contexts.
return num_threads == 1 || num_threads == 0;
}
bool LinuxSandbox::seccomp_bpf_started() const {
return seccomp_bpf_started_;
}
sandbox::SetuidSandboxClient*
LinuxSandbox::setuid_sandbox_client() const {
return setuid_sandbox_client_.get();
}
// For seccomp-legacy, we implement the policy inline, here.
bool LinuxSandbox::StartSeccompLegacy(const std::string& process_type) {
if (!pre_initialized_)
PreinitializeSandbox(process_type);
if (seccomp_legacy_supported() && ShouldEnableSeccompLegacy(process_type)) {
// SupportsSeccompSandbox() returns a cached result, as we already
// called it earlier in the PreinitializeSandbox(). Thus, it is OK for us
// to not pass in a file descriptor for "/proc".
#if defined(SECCOMP_SANDBOX)
if (SupportsSeccompSandbox(-1)) {
StartSeccompSandbox();
LogSandboxStarted("seccomp-legacy");
return true;
}
#endif
}
return false;
}
// For seccomp-bpf, we use the SandboxSeccompBpf class.
bool LinuxSandbox::StartSeccompBpf(const std::string& process_type) {
CHECK(!seccomp_bpf_started_);
if (!pre_initialized_)
PreinitializeSandbox(process_type);
if (seccomp_bpf_supported())
seccomp_bpf_started_ = SandboxSeccompBpf::StartSandbox(process_type);
if (seccomp_bpf_started_)
LogSandboxStarted("seccomp-bpf");
return seccomp_bpf_started_;
}
bool LinuxSandbox::seccomp_legacy_supported() const {
CHECK(pre_initialized_);
return seccomp_legacy_supported_;
}
bool LinuxSandbox::seccomp_bpf_supported() const {
CHECK(pre_initialized_);
return seccomp_bpf_supported_;
}
bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) {
(void) process_type;
#if defined(__x86_64__) && !defined(ADDRESS_SANITIZER)
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kNoSandbox)) {
return false;
}
// Limit the address space to 8GB.
const rlim_t kNewAddressSpaceMaxSize = 0x200000000L;
struct rlimit old_address_space_limit;
if (getrlimit(RLIMIT_AS, &old_address_space_limit))
return false;
// Make sure we don't raise the existing limit.
const struct rlimit new_address_space_limit = {
std::min(old_address_space_limit.rlim_cur, kNewAddressSpaceMaxSize),
std::min(old_address_space_limit.rlim_max, kNewAddressSpaceMaxSize)
};
int rc = setrlimit(RLIMIT_AS, &new_address_space_limit);
return (rc == 0);
#else
return false;
#endif // __x86_64__ && !defined(ADDRESS_SANITIZER)
}
} // namespace content