blob: 415f7e6b6beba5c3e59d4b236d65d9bd36eda44d [file] [log] [blame]
// Copyright 2015 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 "services/service_manager/public/cpp/standalone_service/sandbox_linux.h"
#include <fcntl.h>
#include <sys/syscall.h>
#include <utility>
#include "base/bind.h"
#include "base/debug/leak_annotations.h"
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/rand_util.h"
#include "base/sys_info.h"
#include "sandbox/linux/bpf_dsl/policy.h"
#include "sandbox/linux/bpf_dsl/trap_registry.h"
#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.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/thread_helpers.h"
using sandbox::syscall_broker::BrokerFilePermission;
namespace service_manager {
namespace deprecated {
namespace {
intptr_t SandboxSIGSYSHandler(const struct sandbox::arch_seccomp_data& args,
void* aux) {
RAW_CHECK(aux);
const sandbox::syscall_broker::BrokerProcess* broker_process =
static_cast<const sandbox::syscall_broker::BrokerProcess*>(aux);
switch (args.nr) {
#if !defined(__aarch64__)
case __NR_access:
return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
static_cast<int>(args.args[1]));
case __NR_open:
return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
static_cast<int>(args.args[1]));
#endif
case __NR_faccessat:
if (static_cast<int>(args.args[0]) == AT_FDCWD) {
return broker_process->Access(
reinterpret_cast<const char*>(args.args[1]),
static_cast<int>(args.args[2]));
} else {
return -EPERM;
}
case __NR_openat:
// Allow using openat() as open().
if (static_cast<int>(args.args[0]) == AT_FDCWD) {
return broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
static_cast<int>(args.args[2]));
} else {
return -EPERM;
}
default:
RAW_CHECK(false);
return -ENOSYS;
}
}
class SandboxPolicy : public sandbox::BaselinePolicy {
public:
explicit SandboxPolicy(sandbox::syscall_broker::BrokerProcess* broker_process)
: broker_process_(broker_process) {}
~SandboxPolicy() override {}
// Overridden from sandbox::bpf_dsl::Policy:
sandbox::bpf_dsl::ResultExpr EvaluateSyscall(int sysno) const override {
// This policy is only advisory/for noticing FS access for the moment.
switch (sysno) {
#if !defined(__aarch64__)
case __NR_access:
case __NR_open:
#endif
case __NR_faccessat:
case __NR_openat:
return sandbox::bpf_dsl::Trap(SandboxSIGSYSHandler, broker_process_);
case __NR_sched_getaffinity:
return sandbox::RestrictSchedTarget(policy_pid(), sysno);
case __NR_ftruncate:
#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
defined(__aarch64__)
// Per #ifdefs in
// services/service_manager/sandbox/linux/bpf_renderer_policy_linux.cc
case __NR_getrlimit:
#endif
#if defined(__i386__) || defined(__arm__)
case __NR_ugetrlimit:
#endif
case __NR_uname:
#if defined(__arm__) || defined(__x86_64__) || defined(__mips__)
case __NR_getsockopt:
case __NR_setsockopt:
#endif
return sandbox::bpf_dsl::Allow();
}
return BaselinePolicy::EvaluateSyscall(sysno);
}
private:
// Not owned.
const sandbox::syscall_broker::BrokerProcess* broker_process_;
DISALLOW_COPY_AND_ASSIGN(SandboxPolicy);
};
} // namespace
SandboxLinux::SandboxLinux(const std::vector<BrokerFilePermission>& permissions)
: broker_(new sandbox::syscall_broker::BrokerProcess(EPERM, permissions)) {
CHECK(broker_->Init(
base::Bind<bool (*)()>(&sandbox::Credentials::DropAllCapabilities)));
policy_ = std::make_unique<SandboxPolicy>(broker_.get());
}
SandboxLinux::~SandboxLinux() {}
void SandboxLinux::Warmup() {
proc_fd_ = sandbox::ProcUtil::OpenProc();
warmed_up_ = true;
// Verify that we haven't started threads or grabbed directory file
// descriptors.
sandbox::ThreadHelpers::AssertSingleThreaded(proc_fd_.get());
CHECK(!sandbox::ProcUtil::HasOpenDirectory(proc_fd_.get()));
}
void SandboxLinux::EngageNamespaceSandbox() {
CHECK(warmed_up_);
CHECK_EQ(1, getpid());
CHECK(sandbox::NamespaceSandbox::InNewPidNamespace());
CHECK(sandbox::Credentials::MoveToNewUserNS());
CHECK(sandbox::Credentials::DropFileSystemAccess(proc_fd_.get()));
CHECK(sandbox::Credentials::DropAllCapabilities(proc_fd_.get()));
}
void SandboxLinux::EngageSeccompSandbox() {
CHECK(warmed_up_);
sandbox::SandboxBPF sandbox(std::move(policy_));
base::ScopedFD proc_fd(HANDLE_EINTR(
openat(proc_fd_.get(), ".", O_RDONLY | O_DIRECTORY | O_CLOEXEC)));
CHECK(proc_fd.is_valid());
sandbox.SetProcFd(std::move(proc_fd));
CHECK(
sandbox.StartSandbox(sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED))
<< "Starting the process with a sandbox failed. Missing kernel support.";
// The Broker is now bound to this process and should only be destroyed when
// the process exits or is killed.
sandbox::syscall_broker::BrokerProcess* leaked_broker = broker_.release();
ALLOW_UNUSED_LOCAL(leaked_broker);
ANNOTATE_LEAKING_OBJECT_PTR(leaked_broker);
}
void SandboxLinux::Seal() {
proc_fd_.reset();
}
} // namespace deprecated
} // namespace service_manager