| // 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 "sandbox/linux/syscall_broker/broker_process.h" |
| |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <sys/stat.h> |
| #include <sys/syscall.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| |
| #include <algorithm> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/callback.h" |
| #include "base/logging.h" |
| #include "base/posix/eintr_wrapper.h" |
| #include "base/process/process_metrics.h" |
| #include "build/build_config.h" |
| #include "sandbox/linux/syscall_broker/broker_channel.h" |
| #include "sandbox/linux/syscall_broker/broker_client.h" |
| #include "sandbox/linux/syscall_broker/broker_host.h" |
| |
| namespace sandbox { |
| |
| namespace syscall_broker { |
| |
| BrokerProcess::BrokerProcess( |
| int denied_errno, |
| const syscall_broker::BrokerCommandSet& allowed_command_set, |
| const std::vector<syscall_broker::BrokerFilePermission>& permissions, |
| bool fast_check_in_client, |
| bool quiet_failures_for_tests) |
| : initialized_(false), |
| broker_pid_(-1), |
| fast_check_in_client_(fast_check_in_client), |
| quiet_failures_for_tests_(quiet_failures_for_tests), |
| allowed_command_set_(allowed_command_set), |
| broker_permission_list_(denied_errno, permissions) {} |
| |
| BrokerProcess::~BrokerProcess() { |
| if (initialized_) { |
| if (broker_client_.get()) { |
| // Closing the socket should be enough to notify the child to die, |
| // unless it has been duplicated. |
| CloseChannel(); |
| } |
| PCHECK(0 == kill(broker_pid_, SIGKILL)); |
| siginfo_t process_info; |
| // Reap the child. |
| int ret = HANDLE_EINTR(waitid(P_PID, broker_pid_, &process_info, WEXITED)); |
| PCHECK(0 == ret); |
| } |
| } |
| |
| bool BrokerProcess::Init( |
| const base::Callback<bool(void)>& broker_process_init_callback) { |
| CHECK(!initialized_); |
| BrokerChannel::EndPoint ipc_reader; |
| BrokerChannel::EndPoint ipc_writer; |
| BrokerChannel::CreatePair(&ipc_reader, &ipc_writer); |
| |
| #if !defined(THREAD_SANITIZER) |
| DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle())); |
| #endif |
| int child_pid = fork(); |
| if (child_pid == -1) |
| return false; |
| |
| if (child_pid) { |
| // We are the parent and we have just forked our broker process. |
| ipc_reader.reset(); |
| broker_pid_ = child_pid; |
| broker_client_ = std::make_unique<BrokerClient>( |
| broker_permission_list_, std::move(ipc_writer), allowed_command_set_, |
| fast_check_in_client_, quiet_failures_for_tests_); |
| initialized_ = true; |
| return true; |
| } |
| |
| // We are the broker process. Make sure to close the writer's end so that |
| // we get notified if the client disappears. |
| ipc_writer.reset(); |
| CHECK(broker_process_init_callback.Run()); |
| BrokerHost broker_host(broker_permission_list_, allowed_command_set_, |
| std::move(ipc_reader)); |
| for (;;) { |
| switch (broker_host.HandleRequest()) { |
| case BrokerHost::RequestStatus::LOST_CLIENT: |
| _exit(1); |
| case BrokerHost::RequestStatus::SUCCESS: |
| case BrokerHost::RequestStatus::FAILURE: |
| continue; |
| } |
| } |
| _exit(1); |
| NOTREACHED(); |
| return false; |
| } |
| |
| void BrokerProcess::CloseChannel() { |
| broker_client_.reset(); |
| } |
| |
| int BrokerProcess::Access(const char* pathname, int mode) const { |
| RAW_CHECK(initialized_); |
| return broker_client_->Access(pathname, mode); |
| } |
| |
| int BrokerProcess::Mkdir(const char* path, int mode) const { |
| RAW_CHECK(initialized_); |
| return broker_client_->Mkdir(path, mode); |
| } |
| |
| int BrokerProcess::Open(const char* pathname, int flags) const { |
| RAW_CHECK(initialized_); |
| return broker_client_->Open(pathname, flags); |
| } |
| |
| int BrokerProcess::Readlink(const char* path, char* buf, size_t bufsize) const { |
| RAW_CHECK(initialized_); |
| return broker_client_->Readlink(path, buf, bufsize); |
| } |
| |
| int BrokerProcess::Rename(const char* oldpath, const char* newpath) const { |
| RAW_CHECK(initialized_); |
| return broker_client_->Rename(oldpath, newpath); |
| } |
| |
| int BrokerProcess::Rmdir(const char* pathname) const { |
| RAW_CHECK(initialized_); |
| return broker_client_->Rmdir(pathname); |
| } |
| |
| int BrokerProcess::Stat(const char* pathname, struct stat* sb) const { |
| RAW_CHECK(initialized_); |
| return broker_client_->Stat(pathname, sb); |
| } |
| |
| int BrokerProcess::Stat64(const char* pathname, struct stat64* sb) const { |
| RAW_CHECK(initialized_); |
| return broker_client_->Stat64(pathname, sb); |
| } |
| |
| int BrokerProcess::Unlink(const char* pathname) const { |
| RAW_CHECK(initialized_); |
| return broker_client_->Unlink(pathname); |
| } |
| |
| #if defined(MEMORY_SANITIZER) |
| #define BROKER_UNPOISON_STRING(x) __msan_unpoison_string(x) |
| #else |
| #define BROKER_UNPOISON_STRING(x) |
| #endif |
| |
| // static |
| intptr_t BrokerProcess::SIGSYS_Handler(const sandbox::arch_seccomp_data& args, |
| void* aux_broker_process) { |
| RAW_CHECK(aux_broker_process); |
| auto* broker_process = static_cast<BrokerProcess*>(aux_broker_process); |
| switch (args.nr) { |
| #if defined(__NR_access) |
| case __NR_access: |
| return broker_process->Access(reinterpret_cast<const char*>(args.args[0]), |
| static_cast<int>(args.args[1])); |
| #endif |
| #if defined(__NR_faccessat) |
| case __NR_faccessat: |
| if (static_cast<int>(args.args[0]) != AT_FDCWD) |
| return -EPERM; |
| return broker_process->Access(reinterpret_cast<const char*>(args.args[1]), |
| static_cast<int>(args.args[2])); |
| #endif |
| #if defined(__NR_mkdir) |
| case __NR_mkdir: |
| return broker_process->Mkdir(reinterpret_cast<const char*>(args.args[0]), |
| static_cast<int>(args.args[1])); |
| #endif |
| #if defined(__NR_mkdirat) |
| case __NR_mkdirat: |
| if (static_cast<int>(args.args[0]) != AT_FDCWD) |
| return -EPERM; |
| return broker_process->Mkdir(reinterpret_cast<const char*>(args.args[1]), |
| static_cast<int>(args.args[2])); |
| #endif |
| #if defined(__NR_open) |
| case __NR_open: |
| // http://crbug.com/372840 |
| BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0])); |
| return broker_process->Open(reinterpret_cast<const char*>(args.args[0]), |
| static_cast<int>(args.args[1])); |
| #endif |
| #if defined(__NR_openat) |
| case __NR_openat: |
| if (static_cast<int>(args.args[0]) != AT_FDCWD) |
| return -EPERM; |
| // http://crbug.com/372840 |
| BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[1])); |
| return broker_process->Open(reinterpret_cast<const char*>(args.args[1]), |
| static_cast<int>(args.args[2])); |
| #endif |
| #if defined(__NR_readlink) |
| case __NR_readlink: |
| return broker_process->Readlink( |
| reinterpret_cast<const char*>(args.args[0]), |
| reinterpret_cast<char*>(args.args[1]), |
| static_cast<size_t>(args.args[2])); |
| #endif |
| #if defined(__NR_readlinkat) |
| case __NR_readlinkat: |
| if (static_cast<int>(args.args[0]) != AT_FDCWD) |
| return -EPERM; |
| return broker_process->Readlink( |
| reinterpret_cast<const char*>(args.args[1]), |
| reinterpret_cast<char*>(args.args[2]), |
| static_cast<size_t>(args.args[3])); |
| #endif |
| #if defined(__NR_rename) |
| case __NR_rename: |
| return broker_process->Rename( |
| reinterpret_cast<const char*>(args.args[0]), |
| reinterpret_cast<const char*>(args.args[1])); |
| #endif |
| #if defined(__NR_renameat) |
| case __NR_renameat: |
| if (static_cast<int>(args.args[0]) != AT_FDCWD || |
| static_cast<int>(args.args[2]) != AT_FDCWD) { |
| return -EPERM; |
| } |
| return broker_process->Rename( |
| reinterpret_cast<const char*>(args.args[1]), |
| reinterpret_cast<const char*>(args.args[3])); |
| #endif |
| #if defined(__NR_renameat2) |
| case __NR_renameat2: |
| if (static_cast<int>(args.args[0]) != AT_FDCWD || |
| static_cast<int>(args.args[2]) != AT_FDCWD) { |
| return -EPERM; |
| } |
| if (static_cast<int>(args.args[4]) != 0) |
| return -EINVAL; |
| return broker_process->Rename( |
| reinterpret_cast<const char*>(args.args[1]), |
| reinterpret_cast<const char*>(args.args[3])); |
| #endif |
| #if defined(__NR_rmdir) |
| case __NR_rmdir: |
| return broker_process->Rmdir(reinterpret_cast<const char*>(args.args[0])); |
| #endif |
| #if defined(__NR_stat) |
| case __NR_stat: |
| return broker_process->Stat(reinterpret_cast<const char*>(args.args[0]), |
| reinterpret_cast<struct stat*>(args.args[1])); |
| #endif |
| #if defined(__NR_stat64) |
| case __NR_stat64: |
| return broker_process->Stat64( |
| reinterpret_cast<const char*>(args.args[0]), |
| reinterpret_cast<struct stat64*>(args.args[1])); |
| #endif |
| #if defined(__NR_fstatat) |
| case __NR_fstatat: |
| if (static_cast<int>(args.args[0]) != AT_FDCWD) |
| return -EPERM; |
| if (static_cast<int>(args.args[3]) != 0) |
| return -EINVAL; |
| return broker_process->Stat(reinterpret_cast<const char*>(args.args[1]), |
| reinterpret_cast<struct stat*>(args.args[2])); |
| #endif |
| #if defined(__NR_newfstatat) |
| case __NR_newfstatat: |
| if (static_cast<int>(args.args[0]) != AT_FDCWD) |
| return -EPERM; |
| if (static_cast<int>(args.args[3]) != 0) |
| return -EINVAL; |
| return broker_process->Stat(reinterpret_cast<const char*>(args.args[1]), |
| reinterpret_cast<struct stat*>(args.args[2])); |
| #endif |
| #if defined(__NR_unlink) |
| case __NR_unlink: |
| return broker_process->Unlink( |
| reinterpret_cast<const char*>(args.args[0])); |
| #endif |
| #if defined(__NR_unlinkat) |
| case __NR_unlinkat: |
| // TODO(tsepez): does not support AT_REMOVEDIR flag. |
| if (static_cast<int>(args.args[0]) != AT_FDCWD) |
| return -EPERM; |
| return broker_process->Unlink( |
| reinterpret_cast<const char*>(args.args[1])); |
| #endif |
| default: |
| RAW_CHECK(false); |
| return -ENOSYS; |
| } |
| } |
| |
| } // namespace syscall_broker |
| } // namespace sandbox |