| // 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 |
| |