diff options
Diffstat (limited to 'chromium/content/common/sandbox_linux/sandbox_linux.cc')
-rw-r--r-- | chromium/content/common/sandbox_linux/sandbox_linux.cc | 166 |
1 files changed, 107 insertions, 59 deletions
diff --git a/chromium/content/common/sandbox_linux/sandbox_linux.cc b/chromium/content/common/sandbox_linux/sandbox_linux.cc index 12e11d8e131..b2a7b3e6e96 100644 --- a/chromium/content/common/sandbox_linux/sandbox_linux.cc +++ b/chromium/content/common/sandbox_linux/sandbox_linux.cc @@ -11,6 +11,8 @@ #include <unistd.h> #include <limits> +#include <string> +#include <vector> #include "base/bind.h" #include "base/callback_helpers.h" @@ -26,11 +28,15 @@ #include "base/sys_info.h" #include "base/time/time.h" #include "build/build_config.h" +#include "content/common/sandbox_linux/sandbox_debug_handling_linux.h" #include "content/common/sandbox_linux/sandbox_linux.h" #include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h" #include "content/public/common/content_switches.h" #include "content/public/common/sandbox_linux.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/resource_limits.h" #include "sandbox/linux/services/thread_helpers.h" #include "sandbox/linux/services/yama.h" #include "sandbox/linux/suid/client/setuid_sandbox_client.h" @@ -62,19 +68,6 @@ void LogSandboxStarted(const std::string& sandbox_name) { VLOG(1) << activated_sandbox; } -bool AddResourceLimit(int resource, rlim_t limit) { - struct rlimit old_rlimit; - if (getrlimit(resource, &old_rlimit)) - return false; - // Make sure we don't raise the existing limit. - const struct rlimit new_rlimit = { - std::min(old_rlimit.rlim_cur, limit), - std::min(old_rlimit.rlim_max, limit) - }; - int rc = setrlimit(resource, &new_rlimit); - return rc == 0; -} - bool IsRunningTSAN() { #if defined(THREAD_SANITIZER) return true; @@ -83,19 +76,23 @@ bool IsRunningTSAN() { #endif } -// Try to open /proc/self/task/ with the help of |proc_fd|. |proc_fd| can be -// -1. Will return -1 on error and set errno like open(2). -int OpenProcTaskFd(int proc_fd) { - int proc_self_task = -1; +// Get a file descriptor to /proc. Either duplicate |proc_fd| or try to open +// it by using the filesystem directly. +// TODO(jln): get rid of this ugly interface. +base::ScopedFD OpenProc(int proc_fd) { + int ret_proc_fd = -1; if (proc_fd >= 0) { // If a handle to /proc is available, use it. This allows to bypass file // system restrictions. - proc_self_task = openat(proc_fd, "self/task/", O_RDONLY | O_DIRECTORY); + ret_proc_fd = + HANDLE_EINTR(openat(proc_fd, ".", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); } else { // Otherwise, make an attempt to access the file system directly. - proc_self_task = open("/proc/self/task/", O_RDONLY | O_DIRECTORY); + ret_proc_fd = HANDLE_EINTR( + openat(AT_FDCWD, "/proc/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); } - return proc_self_task; + DCHECK_LE(0, ret_proc_fd); + return base::ScopedFD(ret_proc_fd); } } // namespace @@ -108,9 +105,10 @@ LinuxSandbox::LinuxSandbox() sandbox_status_flags_(kSandboxLinuxInvalid), pre_initialized_(false), seccomp_bpf_supported_(false), + seccomp_bpf_with_tsync_supported_(false), yama_is_enforcing_(false), - setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) -{ + initialize_sandbox_ran_(false), + setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) { if (setuid_sandbox_client_ == NULL) { LOG(FATAL) << "Failed to instantiate the setuid sandbox client."; } @@ -121,6 +119,9 @@ LinuxSandbox::LinuxSandbox() } LinuxSandbox::~LinuxSandbox() { + if (pre_initialized_) { + CHECK(initialize_sandbox_ran_); + } } LinuxSandbox* LinuxSandbox::GetInstance() { @@ -144,12 +145,14 @@ void LinuxSandbox::PreinitializeSandbox() { // its contents before the sandbox is enabled. It also pre-opens the // object files that are already loaded in the process address space. base::debug::EnableInProcessStackDumpingForSandbox(); +#endif // !defined(NDEBUG) - // Open proc_fd_ only in Debug mode so that forgetting to close it doesn't - // produce a sandbox escape in Release mode. - proc_fd_ = open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC); + // Open proc_fd_. It would break the security of the setuid sandbox if it was + // not closed. + // If LinuxSandbox::PreinitializeSandbox() runs, InitializeSandbox() must run + // as well. + proc_fd_ = HANDLE_EINTR(open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC)); CHECK_GE(proc_fd_, 0); -#endif // !defined(NDEBUG) // We "pre-warm" the code that detects supports for seccomp BPF. if (SandboxSeccompBPF::IsSeccompBPFDesired()) { if (!SandboxSeccompBPF::SupportsSandbox()) { @@ -157,6 +160,10 @@ void LinuxSandbox::PreinitializeSandbox() { } else { seccomp_bpf_supported_ = true; } + + if (SandboxSeccompBPF::SupportsSandboxWithTsync()) { + seccomp_bpf_with_tsync_supported_ = true; + } } // Yama is a "global", system-level status. We assume it will not regress @@ -167,6 +174,38 @@ void LinuxSandbox::PreinitializeSandbox() { pre_initialized_ = true; } +void LinuxSandbox::EngageNamespaceSandbox() { + CHECK(pre_initialized_); + // Check being in a new PID namespace created by the namespace sandbox and + // being the init process. + CHECK(sandbox::NamespaceSandbox::InNewPidNamespace()); + const pid_t pid = getpid(); + CHECK_EQ(1, pid); + + CHECK(sandbox::Credentials::MoveToNewUserNS()); + // Note: this requires SealSandbox() to be called later in this process to be + // safe, as this class is keeping a file descriptor to /proc/. + CHECK(sandbox::Credentials::DropFileSystemAccess(proc_fd_)); + + // We do not drop CAP_SYS_ADMIN because we need it to place each child process + // in its own PID namespace later on. + std::vector<sandbox::Credentials::Capability> caps; + caps.push_back(sandbox::Credentials::Capability::SYS_ADMIN); + CHECK(sandbox::Credentials::SetCapabilities(proc_fd_, caps)); + + // This needs to happen after moving to a new user NS, since doing so involves + // writing the UID/GID map. + CHECK(SandboxDebugHandling::SetDumpableStatusAndHandlers()); +} + +std::vector<int> LinuxSandbox::GetFileDescriptorsToClose() { + std::vector<int> fds; + if (proc_fd_ >= 0) { + fds.push_back(proc_fd_); + } + return fds; +} + bool LinuxSandbox::InitializeSandbox() { LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance(); return linux_sandbox->InitializeSandboxImpl(); @@ -178,7 +217,9 @@ void LinuxSandbox::StopThread(base::Thread* thread) { } int LinuxSandbox::GetStatus() { - CHECK(pre_initialized_); + if (!pre_initialized_) { + return 0; + } if (kSandboxLinuxInvalid == sandbox_status_flags_) { // Initialize sandbox_status_flags_. sandbox_status_flags_ = 0; @@ -188,6 +229,12 @@ int LinuxSandbox::GetStatus() { sandbox_status_flags_ |= kSandboxLinuxPIDNS; if (setuid_sandbox_client_->IsInNewNETNamespace()) sandbox_status_flags_ |= kSandboxLinuxNetNS; + } else if (sandbox::NamespaceSandbox::InNewUserNamespace()) { + sandbox_status_flags_ |= kSandboxLinuxUserNS; + if (sandbox::NamespaceSandbox::InNewPidNamespace()) + sandbox_status_flags_ |= kSandboxLinuxPIDNS; + if (sandbox::NamespaceSandbox::InNewNetNamespace()) + sandbox_status_flags_ |= kSandboxLinuxNetNS; } // We report whether the sandbox will be activated when renderers, workers @@ -197,6 +244,11 @@ int LinuxSandbox::GetStatus() { sandbox_status_flags_ |= kSandboxLinuxSeccompBPF; } + if (seccomp_bpf_with_tsync_supported() && + SandboxSeccompBPF::ShouldEnableSeccompBPF(switches::kRendererProcess)) { + sandbox_status_flags_ |= kSandboxLinuxSeccompTSYNC; + } + if (yama_is_enforcing_) { sandbox_status_flags_ |= kSandboxLinuxYama; } @@ -209,27 +261,13 @@ int LinuxSandbox::GetStatus() { // PID namespaces and existing sandboxes, so "self" must really be used instead // of using the pid. bool LinuxSandbox::IsSingleThreaded() const { - bool is_single_threaded = false; - base::ScopedFD proc_self_task(OpenProcTaskFd(proc_fd_)); + base::ScopedFD proc_fd(OpenProc(proc_fd_)); -// In Debug mode, it's mandatory to be able to count threads to catch bugs. -#if !defined(NDEBUG) - // Using CHECK here since we want to check all the cases where - // !defined(NDEBUG) - // gets built. - CHECK(proc_self_task.is_valid()) - << "Could not count threads, the sandbox was not " - << "pre-initialized properly."; -#endif // !defined(NDEBUG) + CHECK(proc_fd.is_valid()) << "Could not count threads, the sandbox was not " + << "pre-initialized properly."; - if (!proc_self_task.is_valid()) { - // Pretend to be monothreaded if it can't be determined (for instance the - // setuid sandbox is already engaged but no proc_fd_ is available). - is_single_threaded = true; - } else { - is_single_threaded = - sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task.get()); - } + const bool is_single_threaded = + sandbox::ThreadHelpers::IsSingleThreaded(proc_fd.get()); return is_single_threaded; } @@ -247,16 +285,22 @@ sandbox::SetuidSandboxClient* bool LinuxSandbox::StartSeccompBPF(const std::string& process_type) { CHECK(!seccomp_bpf_started_); CHECK(pre_initialized_); - if (seccomp_bpf_supported()) - seccomp_bpf_started_ = SandboxSeccompBPF::StartSandbox(process_type); + if (seccomp_bpf_supported()) { + seccomp_bpf_started_ = + SandboxSeccompBPF::StartSandbox(process_type, OpenProc(proc_fd_)); + } - if (seccomp_bpf_started_) + if (seccomp_bpf_started_) { LogSandboxStarted("seccomp-bpf"); + } return seccomp_bpf_started_; } bool LinuxSandbox::InitializeSandboxImpl() { + DCHECK(!initialize_sandbox_ran_); + initialize_sandbox_ran_ = true; + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); const std::string process_type = command_line->GetSwitchValueASCII(switches::kProcessType); @@ -329,6 +373,11 @@ bool LinuxSandbox::seccomp_bpf_supported() const { return seccomp_bpf_supported_; } +bool LinuxSandbox::seccomp_bpf_with_tsync_supported() const { + CHECK(pre_initialized_); + return seccomp_bpf_with_tsync_supported_; +} + bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) { (void) process_type; #if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ @@ -361,17 +410,16 @@ bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) { // allocations that can't be index by an int. const rlim_t kNewDataSegmentMaxSize = std::numeric_limits<int>::max(); - bool limited_as = AddResourceLimit(RLIMIT_AS, address_space_limit); - bool limited_data = AddResourceLimit(RLIMIT_DATA, kNewDataSegmentMaxSize); + bool limited_as = + sandbox::ResourceLimits::Lower(RLIMIT_AS, address_space_limit); + bool limited_data = + sandbox::ResourceLimits::Lower(RLIMIT_DATA, kNewDataSegmentMaxSize); // Cache the resource limit before turning on the sandbox. base::SysInfo::AmountOfVirtualMemory(); return limited_as && limited_data; #else - // Silence the compiler warning about unused function. This doesn't actually - // call AddResourceLimit(). - ignore_result(AddResourceLimit); base::SysInfo::AmountOfVirtualMemory(); return false; #endif // !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && @@ -379,7 +427,7 @@ bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) { } bool LinuxSandbox::HasOpenDirectories() const { - return sandbox::Credentials().HasOpenDirectory(proc_fd_); + return sandbox::ProcUtil::HasOpenDirectory(proc_fd_); } void LinuxSandbox::SealSandbox() { @@ -406,10 +454,10 @@ void LinuxSandbox::CheckForBrokenPromises(const std::string& process_type) { void LinuxSandbox::StopThreadAndEnsureNotCounted(base::Thread* thread) const { DCHECK(thread); - base::ScopedFD proc_self_task(OpenProcTaskFd(proc_fd_)); - PCHECK(proc_self_task.is_valid()); - CHECK(sandbox::ThreadHelpers::StopThreadAndWatchProcFS(proc_self_task.get(), - thread)); + base::ScopedFD proc_fd(OpenProc(proc_fd_)); + PCHECK(proc_fd.is_valid()); + CHECK( + sandbox::ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.get(), thread)); } } // namespace content |