#include <fcntl.h>
#include <sys/stat.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
}
bool IsSeccompLegacyDesired() {
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kNoSandbox)) {
return false;
}
#if defined(SECCOMP_SANDBOX)
#if defined(NDEBUG)
return command_line->HasSwitch(switches::kEnableSeccompSandbox);
#else
return !command_line->HasSwitch(switches::kDisableSeccompSandbox);
#endif
#endif
return false;
}
bool ShouldEnableSeccompLegacy(const std::string& process_type) {
if (IsSeccompLegacyDesired() &&
process_type == switches::kRendererProcess) {
return true;
} else {
return false;
}
}
}
namespace content {
LinuxSandbox::LinuxSandbox()
: proc_fd_(-1),
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.";
} 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
if (SandboxSeccompBpf::IsSeccompBpfDesired()) {
if (!SandboxSeccompBpf::SupportsSandbox()) {
VLOG(1) << "Lacking support for seccomp-bpf sandbox.";
} else {
seccomp_bpf_supported_ = true;
}
}
pre_initialized_ = true;
}
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)) {
sandbox_flags |= kSandboxLinuxSeccompBpf;
}
if (!(sandbox_flags & kSandboxLinuxSeccompBpf) &&
seccomp_legacy_supported() &&
ShouldEnableSeccompLegacy(switches::kRendererProcess)) {
sandbox_flags |= kSandboxLinuxSeccompLegacy;
}
return sandbox_flags;
}
bool LinuxSandbox::IsSingleThreaded() const {
int num_threads = file_util::CountFilesCreatedAfter(
FilePath("/proc/self/task"),
base::Time::UnixEpoch());
return num_threads == 1 || num_threads == 0;
}
sandbox::SetuidSandboxClient*
LinuxSandbox::setuid_sandbox_client() const {
return setuid_sandbox_client_.get();
}
bool LinuxSandbox::StartSeccompLegacy(const std::string& process_type) {
if (!pre_initialized_)
PreinitializeSandbox(process_type);
if (seccomp_legacy_supported() && ShouldEnableSeccompLegacy(process_type)) {
#if defined(SECCOMP_SANDBOX)
if (SupportsSeccompSandbox(-1)) {
StartSeccompSandbox();
LogSandboxStarted("seccomp-legacy");
return true;
}
#endif
}
return false;
}
bool LinuxSandbox::StartSeccompBpf(const std::string& process_type) {
if (!pre_initialized_)
PreinitializeSandbox(process_type);
bool started_bpf_sandbox = false;
if (seccomp_bpf_supported())
started_bpf_sandbox = SandboxSeccompBpf::StartSandbox(process_type);
if (started_bpf_sandbox)
LogSandboxStarted("seccomp-bpf");
return started_bpf_sandbox;
}
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_;
}
}