mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Split PtraceMonitor into separate file
PiperOrigin-RevId: 505660957 Change-Id: I6b8fcbb86c9fef294b6d19e2d1ec7120415f843b
This commit is contained in:
parent
97d67019d2
commit
8f24f2a4f0
|
@ -309,8 +309,8 @@ cc_library(
|
|||
cc_library(
|
||||
name = "sandbox2",
|
||||
srcs = [
|
||||
"monitor.cc",
|
||||
"monitor.h",
|
||||
"monitor_ptrace.cc",
|
||||
"monitor_ptrace.h",
|
||||
"sandbox2.cc",
|
||||
"stack_trace.cc",
|
||||
"stack_trace.h",
|
||||
|
@ -334,11 +334,10 @@ cc_library(
|
|||
":comms",
|
||||
":executor",
|
||||
":fork_client",
|
||||
":forkserver_cc_proto",
|
||||
":global_forkserver",
|
||||
":ipc",
|
||||
":limits",
|
||||
":logsink",
|
||||
":monitor_base",
|
||||
":mounts",
|
||||
":namespace",
|
||||
":notify",
|
||||
|
@ -353,7 +352,6 @@ cc_library(
|
|||
"//sandboxed_api:config",
|
||||
"//sandboxed_api/sandbox2/network_proxy:client",
|
||||
"//sandboxed_api/sandbox2/network_proxy:filtering",
|
||||
"//sandboxed_api/sandbox2/network_proxy:server",
|
||||
"//sandboxed_api/sandbox2/unwind",
|
||||
"//sandboxed_api/sandbox2/unwind:unwind_cc_proto",
|
||||
"//sandboxed_api/sandbox2/util:bpf_helper",
|
||||
|
@ -375,7 +373,6 @@ cc_library(
|
|||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_absl//absl/time",
|
||||
"@com_google_absl//absl/types:optional",
|
||||
|
@ -384,6 +381,42 @@ cc_library(
|
|||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "monitor_base",
|
||||
srcs = ["monitor_base.cc"],
|
||||
hdrs = ["monitor_base.h"],
|
||||
copts = sapi_platform_copts(),
|
||||
deps = [
|
||||
":client",
|
||||
":comms",
|
||||
":executor",
|
||||
":ipc",
|
||||
":limits",
|
||||
":mounts",
|
||||
":namespace",
|
||||
":notify",
|
||||
":policy",
|
||||
":result",
|
||||
":syscall",
|
||||
":util",
|
||||
"//sandboxed_api/sandbox2/network_proxy:server",
|
||||
"//sandboxed_api/util:file_helpers",
|
||||
"//sandboxed_api/util:raw_logging",
|
||||
"//sandboxed_api/util:strerror",
|
||||
"//sandboxed_api/util:temp_file",
|
||||
"@com_google_absl//absl/cleanup",
|
||||
"@com_google_absl//absl/flags:flag",
|
||||
"@com_google_absl//absl/log",
|
||||
"@com_google_absl//absl/log:check",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_absl//absl/time",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "policybuilder",
|
||||
srcs = ["policybuilder.cc"],
|
||||
|
|
|
@ -286,8 +286,8 @@ target_link_libraries(sandbox2_executor
|
|||
|
||||
# sandboxed_api/sandbox2:sandbox2
|
||||
add_library(sandbox2_sandbox2 ${SAPI_LIB_TYPE}
|
||||
monitor.cc
|
||||
monitor.h
|
||||
monitor_ptrace.cc
|
||||
monitor_ptrace.h
|
||||
sandbox2.cc
|
||||
sandbox2.h
|
||||
stack_trace.cc
|
||||
|
@ -298,6 +298,7 @@ target_link_libraries(sandbox2_sandbox2
|
|||
PRIVATE absl::core_headers
|
||||
absl::cleanup
|
||||
absl::flat_hash_set
|
||||
absl::memory
|
||||
absl::optional
|
||||
absl::str_format
|
||||
absl::strings
|
||||
|
@ -318,11 +319,11 @@ target_link_libraries(sandbox2_sandbox2
|
|||
sandbox2::comms
|
||||
sandbox2::executor
|
||||
sandbox2::fork_client
|
||||
sandbox2::forkserver_proto
|
||||
sandbox2::global_forkserver
|
||||
sandbox2::ipc
|
||||
sandbox2::limits
|
||||
sandbox2::logsink
|
||||
sandbox2::monitor_base
|
||||
sandbox2::mounts
|
||||
sandbox2::mount_tree_proto
|
||||
sandbox2::namespace
|
||||
|
@ -341,6 +342,37 @@ target_link_libraries(sandbox2_sandbox2
|
|||
sandbox2::violation_proto
|
||||
)
|
||||
|
||||
# sandboxed_api/sandbox2:monitor_base
|
||||
add_library(sandbox2_monitor_base ${SAPI_LIB_TYPE}
|
||||
monitor_base.cc
|
||||
monitor_base.h
|
||||
)
|
||||
add_library(sandbox2::monitor_base ALIAS sandbox2_monitor_base)
|
||||
target_link_libraries(sandbox2_monitor_base
|
||||
PRIVATE absl::status
|
||||
absl::synchronization
|
||||
sandbox2::comms
|
||||
sandbox2::executor
|
||||
sandbox2::ipc
|
||||
sandbox2::network_proxy_server
|
||||
sandbox2::notify
|
||||
sandbox2::policy
|
||||
sandbox2::result
|
||||
sandbox2::syscall
|
||||
sapi::base
|
||||
sapi::raw_logging
|
||||
PUBLIC absl::cleanup
|
||||
absl::statusor
|
||||
absl::time
|
||||
sapi::file_helpers
|
||||
sapi::temp_file
|
||||
sandbox2::client
|
||||
sandbox2::limits
|
||||
sandbox2::mounts
|
||||
sandbox2::namespace
|
||||
sandbox2::util
|
||||
)
|
||||
|
||||
# sandboxed_api/sandbox2:policybuilder
|
||||
add_library(sandbox2_policybuilder ${SAPI_LIB_TYPE}
|
||||
policybuilder.cc
|
||||
|
|
395
sandboxed_api/sandbox2/monitor_base.cc
Normal file
395
sandboxed_api/sandbox2/monitor_base.cc
Normal file
|
@ -0,0 +1,395 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Implementation file for the sandbox2::MonitorBase class.
|
||||
|
||||
#include "sandboxed_api/sandbox2/monitor_base.h"
|
||||
|
||||
#include <sched.h>
|
||||
#include <syscall.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/cleanup/cleanup.h"
|
||||
#include "absl/flags/declare.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "sandboxed_api/sandbox2/client.h"
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/sandbox2/executor.h"
|
||||
#include "sandboxed_api/sandbox2/limits.h"
|
||||
#include "sandboxed_api/sandbox2/mounts.h"
|
||||
#include "sandboxed_api/sandbox2/namespace.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/server.h"
|
||||
#include "sandboxed_api/sandbox2/policy.h"
|
||||
#include "sandboxed_api/sandbox2/result.h"
|
||||
#include "sandboxed_api/sandbox2/syscall.h"
|
||||
#include "sandboxed_api/sandbox2/util.h"
|
||||
#include "sandboxed_api/util/file_helpers.h"
|
||||
#include "sandboxed_api/util/raw_logging.h"
|
||||
#include "sandboxed_api/util/strerror.h"
|
||||
#include "sandboxed_api/util/temp_file.h"
|
||||
|
||||
ABSL_FLAG(bool, sandbox2_report_on_sandboxee_signal, true,
|
||||
"Report sandbox2 sandboxee deaths caused by signals");
|
||||
|
||||
ABSL_FLAG(bool, sandbox2_report_on_sandboxee_timeout, true,
|
||||
"Report sandbox2 sandboxee timeouts");
|
||||
|
||||
ABSL_DECLARE_FLAG(bool, sandbox2_danger_danger_permit_all);
|
||||
ABSL_DECLARE_FLAG(std::string, sandbox2_danger_danger_permit_all_and_log);
|
||||
|
||||
namespace sandbox2 {
|
||||
namespace {
|
||||
|
||||
void MaybeEnableTomoyoLsmWorkaround(Mounts& mounts, std::string& comms_fd_dev) {
|
||||
static auto tomoyo_active = []() -> bool {
|
||||
std::string lsm_list;
|
||||
if (auto status = sapi::file::GetContents(
|
||||
"/sys/kernel/security/lsm", &lsm_list, sapi::file::Defaults());
|
||||
!status.ok() && !absl::IsNotFound(status)) {
|
||||
VLOG(1) << "Checking active LSMs failed: " << status.message() << ": "
|
||||
<< sapi::StrError(errno);
|
||||
return false;
|
||||
}
|
||||
return absl::StrContains(lsm_list, "tomoyo");
|
||||
}();
|
||||
|
||||
if (!tomoyo_active) {
|
||||
return;
|
||||
}
|
||||
VLOG(1) << "Tomoyo LSM active, enabling workaround";
|
||||
|
||||
if (mounts.ResolvePath("/dev").ok() || mounts.ResolvePath("/dev/fd").ok()) {
|
||||
// Avoid shadowing /dev/fd/1022 below if /dev or /dev/fd is already mapped.
|
||||
VLOG(1) << "Parent dir already mapped, skipping";
|
||||
return;
|
||||
}
|
||||
|
||||
auto temp_file = sapi::CreateNamedTempFileAndClose("/tmp/");
|
||||
if (!temp_file.ok()) {
|
||||
LOG(WARNING) << "Failed to create empty temp file: " << temp_file.status();
|
||||
return;
|
||||
}
|
||||
comms_fd_dev = std::move(*temp_file);
|
||||
|
||||
// Ignore errors here, as the file itself might already be mapped.
|
||||
if (auto status = mounts.AddFileAt(
|
||||
comms_fd_dev, absl::StrCat("/dev/fd/", Comms::kSandbox2TargetExecFD),
|
||||
false);
|
||||
!status.ok()) {
|
||||
VLOG(1) << "Mapping comms FD: %s" << status.message();
|
||||
}
|
||||
}
|
||||
|
||||
void LogContainer(const std::vector<std::string>& container) {
|
||||
for (size_t i = 0; i < container.size(); ++i) {
|
||||
LOG(INFO) << "[" << std::setfill('0') << std::setw(4) << i
|
||||
<< "]=" << container[i];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MonitorBase::MonitorBase(Executor* executor, Policy* policy, Notify* notify)
|
||||
: executor_(executor),
|
||||
notify_(notify),
|
||||
policy_(policy),
|
||||
// NOLINTNEXTLINE clang-diagnostic-deprecated-declarations
|
||||
comms_(executor_->ipc()->comms()),
|
||||
ipc_(executor_->ipc()) {
|
||||
// It's a pre-connected Comms channel, no need to accept new connection.
|
||||
CHECK(comms_->IsConnected());
|
||||
std::string path =
|
||||
absl::GetFlag(FLAGS_sandbox2_danger_danger_permit_all_and_log);
|
||||
if (!path.empty()) {
|
||||
log_file_ = std::fopen(path.c_str(), "a+");
|
||||
PCHECK(log_file_ != nullptr) << "Failed to open log file '" << path << "'";
|
||||
}
|
||||
|
||||
if (auto* ns = policy_->GetNamespace(); ns) {
|
||||
// Check for the Tomoyo LSM, which is active by default in several common
|
||||
// distribution kernels (esp. Debian).
|
||||
MaybeEnableTomoyoLsmWorkaround(ns->mounts(), comms_fd_dev_);
|
||||
}
|
||||
}
|
||||
|
||||
MonitorBase::~MonitorBase() {
|
||||
if (!comms_fd_dev_.empty()) {
|
||||
std::remove(comms_fd_dev_.c_str());
|
||||
}
|
||||
if (log_file_) {
|
||||
std::fclose(log_file_);
|
||||
}
|
||||
if (network_proxy_server_) {
|
||||
network_proxy_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void MonitorBase::OnDone() {
|
||||
if (done_notification_.HasBeenNotified()) {
|
||||
return;
|
||||
}
|
||||
|
||||
notify_->EventFinished(result_);
|
||||
ipc_->InternalCleanupFdMap();
|
||||
done_notification_.Notify();
|
||||
}
|
||||
|
||||
void MonitorBase::Launch() {
|
||||
|
||||
absl::Cleanup process_cleanup = [this] {
|
||||
if (process_.init_pid > 0) {
|
||||
kill(process_.init_pid, SIGKILL);
|
||||
} else if (process_.main_pid > 0) {
|
||||
kill(process_.main_pid, SIGKILL);
|
||||
}
|
||||
};
|
||||
absl::Cleanup monitor_done = [this] { OnDone(); };
|
||||
|
||||
Namespace* ns = policy_->GetNamespace();
|
||||
if (SAPI_VLOG_IS_ON(1) && ns != nullptr) {
|
||||
std::vector<std::string> outside_entries;
|
||||
std::vector<std::string> inside_entries;
|
||||
ns->mounts().RecursivelyListMounts(
|
||||
/*outside_entries=*/&outside_entries,
|
||||
/*inside_entries=*/&inside_entries);
|
||||
VLOG(1) << "Outside entries mapped to chroot:";
|
||||
LogContainer(outside_entries);
|
||||
VLOG(1) << "Inside entries as they appear in chroot:";
|
||||
LogContainer(inside_entries);
|
||||
}
|
||||
|
||||
// Don't trace the child: it will allow to use 'strace -f' with the whole
|
||||
// sandbox master/monitor, which ptrace_attach'es to the child.
|
||||
int clone_flags = CLONE_UNTRACED;
|
||||
|
||||
if (policy_->allowed_hosts_) {
|
||||
EnableNetworkProxyServer();
|
||||
}
|
||||
|
||||
// Get PID of the sandboxee.
|
||||
bool should_have_init = ns && (ns->GetCloneFlags() & CLONE_NEWPID);
|
||||
absl::StatusOr<Executor::Process> process =
|
||||
executor_->StartSubProcess(clone_flags, ns, policy_->capabilities());
|
||||
|
||||
if (!process.ok()) {
|
||||
LOG(ERROR) << "Starting sandboxed subprocess failed: " << process.status();
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_SUBPROCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
process_ = *std::move(process);
|
||||
|
||||
if (process_.main_pid <= 0 || (should_have_init && process_.main_pid <= 0)) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_SUBPROCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!notify_->EventStarted(process_.main_pid, comms_)) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_NOTIFY);
|
||||
return;
|
||||
}
|
||||
if (!InitSendIPC()) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_IPC);
|
||||
return;
|
||||
}
|
||||
if (!InitSendCwd()) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_CWD);
|
||||
return;
|
||||
}
|
||||
if (!InitSendPolicy()) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_POLICY);
|
||||
return;
|
||||
}
|
||||
if (!WaitForSandboxReady()) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_WAIT);
|
||||
return;
|
||||
}
|
||||
if (!InitApplyLimits()) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_LIMITS);
|
||||
return;
|
||||
}
|
||||
std::move(process_cleanup).Cancel();
|
||||
|
||||
RunInternal();
|
||||
std::move(monitor_done).Cancel();
|
||||
}
|
||||
|
||||
absl::StatusOr<Result> MonitorBase::AwaitResultWithTimeout(
|
||||
absl::Duration timeout) {
|
||||
auto done = done_notification_.WaitForNotificationWithTimeout(timeout);
|
||||
if (!done) {
|
||||
return absl::DeadlineExceededError("Sandbox did not finish within timeout");
|
||||
}
|
||||
|
||||
Join();
|
||||
return result_;
|
||||
}
|
||||
|
||||
void MonitorBase::SetExitStatusCode(Result::StatusEnum final_status,
|
||||
uintptr_t reason_code) {
|
||||
CHECK(result_.final_status() == Result::UNSET);
|
||||
result_.SetExitStatusCode(final_status, reason_code);
|
||||
}
|
||||
|
||||
bool MonitorBase::InitSendPolicy() {
|
||||
if (!policy_->SendPolicy(comms_)) {
|
||||
LOG(ERROR) << "Couldn't send policy";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MonitorBase::InitSendCwd() {
|
||||
if (!comms_->SendString(executor_->cwd_)) {
|
||||
PLOG(ERROR) << "Couldn't send cwd";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MonitorBase::InitApplyLimit(pid_t pid, int resource,
|
||||
const rlimit64& rlim) const {
|
||||
#if defined(__ANDROID__)
|
||||
using RlimitResource = int;
|
||||
#else
|
||||
using RlimitResource = __rlimit_resource;
|
||||
#endif
|
||||
|
||||
rlimit64 curr_limit;
|
||||
if (prlimit64(pid, static_cast<RlimitResource>(resource), nullptr,
|
||||
&curr_limit) == -1) {
|
||||
PLOG(ERROR) << "prlimit64(" << pid << ", " << util::GetRlimitName(resource)
|
||||
<< ")";
|
||||
} else if (rlim.rlim_cur > curr_limit.rlim_max) {
|
||||
// In such case, don't update the limits, as it will fail. Just stick to the
|
||||
// current ones (which are already lower than intended).
|
||||
LOG(ERROR) << util::GetRlimitName(resource)
|
||||
<< ": new.current > current.max (" << rlim.rlim_cur << " > "
|
||||
<< curr_limit.rlim_max << "), skipping";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prlimit64(pid, static_cast<RlimitResource>(resource), &rlim, nullptr) ==
|
||||
-1) {
|
||||
PLOG(ERROR) << "prlimit64(" << pid << ", " << util::GetRlimitName(resource)
|
||||
<< ", " << rlim.rlim_cur << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MonitorBase::InitApplyLimits() {
|
||||
Limits* limits = executor_->limits();
|
||||
return InitApplyLimit(process_.main_pid, RLIMIT_AS, limits->rlimit_as()) &&
|
||||
InitApplyLimit(process_.main_pid, RLIMIT_CPU, limits->rlimit_cpu()) &&
|
||||
InitApplyLimit(process_.main_pid, RLIMIT_FSIZE,
|
||||
limits->rlimit_fsize()) &&
|
||||
InitApplyLimit(process_.main_pid, RLIMIT_NOFILE,
|
||||
limits->rlimit_nofile()) &&
|
||||
InitApplyLimit(process_.main_pid, RLIMIT_CORE, limits->rlimit_core());
|
||||
}
|
||||
|
||||
bool MonitorBase::InitSendIPC() { return ipc_->SendFdsOverComms(); }
|
||||
|
||||
bool MonitorBase::WaitForSandboxReady() {
|
||||
uint32_t tmp;
|
||||
if (!comms_->RecvUint32(&tmp)) {
|
||||
LOG(ERROR) << "Couldn't receive 'Client::kClient2SandboxReady' message";
|
||||
return false;
|
||||
}
|
||||
if (tmp != Client::kClient2SandboxReady) {
|
||||
LOG(ERROR) << "Received " << tmp << " != Client::kClient2SandboxReady ("
|
||||
<< Client::kClient2SandboxReady << ")";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MonitorBase::LogSyscallViolation(const Syscall& syscall) const {
|
||||
// Do not unwind libunwind.
|
||||
if (executor_->libunwind_sbox_for_pid_ != 0) {
|
||||
LOG(ERROR) << "Sandbox violation during execution of libunwind: "
|
||||
<< syscall.GetDescription();
|
||||
return;
|
||||
}
|
||||
|
||||
// So, this is an invalid syscall. Will be killed by seccomp-bpf policies as
|
||||
// well, but we should be on a safe side here as well.
|
||||
LOG(ERROR) << "SANDBOX VIOLATION : PID: " << syscall.pid() << ", PROG: '"
|
||||
<< util::GetProgName(syscall.pid())
|
||||
<< "' : " << syscall.GetDescription();
|
||||
if (SAPI_VLOG_IS_ON(1)) {
|
||||
VLOG(1) << "Cmdline: " << util::GetCmdLine(syscall.pid());
|
||||
VLOG(1) << "Task Name: " << util::GetProcStatusLine(syscall.pid(), "Name");
|
||||
VLOG(1) << "Tgid: " << util::GetProcStatusLine(syscall.pid(), "Tgid");
|
||||
}
|
||||
|
||||
LogSyscallViolationExplanation(syscall);
|
||||
}
|
||||
|
||||
void MonitorBase::LogSyscallViolationExplanation(const Syscall& syscall) const {
|
||||
const uintptr_t syscall_nr = syscall.nr();
|
||||
const uintptr_t arg0 = syscall.args()[0];
|
||||
|
||||
// This follows policy in Policy::GetDefaultPolicy - keep it in sync.
|
||||
if (syscall.arch() != Syscall::GetHostArch()) {
|
||||
LOG(ERROR)
|
||||
<< "This is a violation because the syscall was issued because the"
|
||||
<< " sandboxee and executor architectures are different.";
|
||||
return;
|
||||
}
|
||||
if (syscall_nr == __NR_ptrace) {
|
||||
LOG(ERROR)
|
||||
<< "This is a violation because the ptrace syscall would be unsafe in"
|
||||
<< " sandbox2, so it has been blocked.";
|
||||
return;
|
||||
}
|
||||
if (syscall_nr == __NR_bpf) {
|
||||
LOG(ERROR)
|
||||
<< "This is a violation because the bpf syscall would be risky in"
|
||||
<< " a sandbox, so it has been blocked.";
|
||||
return;
|
||||
}
|
||||
if (syscall_nr == __NR_clone && ((arg0 & CLONE_UNTRACED) != 0)) {
|
||||
LOG(ERROR) << "This is a violation because calling clone with CLONE_UNTRACE"
|
||||
<< " would be unsafe in sandbox2, so it has been blocked.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MonitorBase::EnableNetworkProxyServer() {
|
||||
int fd = ipc_->ReceiveFd(NetworkProxyClient::kFDName);
|
||||
|
||||
network_proxy_server_ = std::make_unique<NetworkProxyServer>(
|
||||
fd, &policy_->allowed_hosts_.value(), pthread_self());
|
||||
|
||||
network_proxy_thread_ = std::thread(&NetworkProxyServer::Run,
|
||||
network_proxy_server_.get());
|
||||
}
|
||||
} // namespace sandbox2
|
140
sandboxed_api/sandbox2/monitor_base.h
Normal file
140
sandboxed_api/sandbox2/monitor_base.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// The sandbox2::Monitor class is responsible for tracking the processes, and
|
||||
// displaying their current statuses (syscalls, states, violations).
|
||||
|
||||
#ifndef SANDBOXED_API_SANDBOX2_MONITOR_BASE_H_
|
||||
#define SANDBOXED_API_SANDBOX2_MONITOR_BASE_H_
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/sandbox2/executor.h"
|
||||
#include "sandboxed_api/sandbox2/ipc.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/server.h"
|
||||
#include "sandboxed_api/sandbox2/notify.h"
|
||||
#include "sandboxed_api/sandbox2/policy.h"
|
||||
#include "sandboxed_api/sandbox2/result.h"
|
||||
#include "sandboxed_api/sandbox2/syscall.h"
|
||||
|
||||
namespace sandbox2 {
|
||||
|
||||
class MonitorBase {
|
||||
public:
|
||||
// executor, policy and notify are not owned by the Monitor
|
||||
MonitorBase(Executor* executor, Policy* policy, Notify* notify);
|
||||
|
||||
MonitorBase(const MonitorBase&) = delete;
|
||||
MonitorBase& operator=(const MonitorBase&) = delete;
|
||||
|
||||
virtual ~MonitorBase();
|
||||
|
||||
// Starts the Monitor.
|
||||
void Launch();
|
||||
|
||||
// Getters for private fields.
|
||||
bool IsDone() const { return done_notification_.HasBeenNotified(); }
|
||||
|
||||
// Enable network proxy server, this will start a thread in the sandbox
|
||||
// that waits for connection requests from the sandboxee.
|
||||
void EnableNetworkProxyServer();
|
||||
|
||||
pid_t pid() const { return process_.main_pid; }
|
||||
|
||||
const Result& result() const { return result_; }
|
||||
|
||||
absl::StatusOr<Result> AwaitResultWithTimeout(absl::Duration timeout);
|
||||
|
||||
virtual void Kill() = 0;
|
||||
virtual void DumpStackTrace() = 0;
|
||||
virtual void SetWallTimeLimit(absl::Duration limit) = 0;
|
||||
|
||||
protected:
|
||||
void OnDone();
|
||||
// Sets basic info status and reason code in the result object.
|
||||
void SetExitStatusCode(Result::StatusEnum final_status,
|
||||
uintptr_t reason_code);
|
||||
// Logs a SANDBOX VIOLATION message based on the registers and additional
|
||||
// explanation for the reason of the violation.
|
||||
void LogSyscallViolation(const Syscall& syscall) const;
|
||||
|
||||
// Internal objects, owned by the Sandbox2 object.
|
||||
Executor* executor_;
|
||||
Notify* notify_;
|
||||
Policy* policy_;
|
||||
// The sandboxee process.
|
||||
Executor::Process process_;
|
||||
Result result_;
|
||||
// Comms channel ptr, copied from the Executor object for convenience.
|
||||
Comms* comms_;
|
||||
// Log file specified by
|
||||
// --sandbox_danger_danger_permit_all_and_log flag.
|
||||
FILE* log_file_ = nullptr;
|
||||
// Handle to the class responsible for proxying and validating connect()
|
||||
// requests.
|
||||
std::unique_ptr<NetworkProxyServer> network_proxy_server_;
|
||||
|
||||
private:
|
||||
// Sends Policy to the Client.
|
||||
// Returns success/failure status.
|
||||
bool InitSendPolicy();
|
||||
|
||||
// Waits for the SandboxReady signal from the client.
|
||||
// Returns success/failure status.
|
||||
bool WaitForSandboxReady();
|
||||
|
||||
// Sends information about data exchange channels.
|
||||
bool InitSendIPC();
|
||||
|
||||
// Sends information about the current working directory.
|
||||
bool InitSendCwd();
|
||||
|
||||
// Applies limits on the sandboxee.
|
||||
bool InitApplyLimits();
|
||||
|
||||
// Applies individual limit on the sandboxee.
|
||||
bool InitApplyLimit(pid_t pid, int resource, const rlimit64& rlim) const;
|
||||
|
||||
// Logs an additional explanation for the possible reason of the violation
|
||||
// based on the registers.
|
||||
void LogSyscallViolationExplanation(const Syscall& syscall) const;
|
||||
|
||||
virtual void RunInternal() = 0;
|
||||
virtual void Join() = 0;
|
||||
|
||||
// IPC ptr, used for exchanging data with the sandboxee.
|
||||
IPC* ipc_;
|
||||
|
||||
// The field indicates whether the sandboxing task has been completed (either
|
||||
// successfully or with error).
|
||||
absl::Notification done_notification_;
|
||||
|
||||
// Empty temp file used for mapping the comms fd when the Tomoyo LSM is
|
||||
// active.
|
||||
std::string comms_fd_dev_;
|
||||
|
||||
std::thread network_proxy_thread_;
|
||||
};
|
||||
|
||||
} // namespace sandbox2
|
||||
|
||||
#endif // SANDBOXED_API_SANDBOX2_MONITOR_BASE_H_
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2019 Google LLC
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -12,34 +12,21 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Implementation file for the sandbox2::Monitor class.
|
||||
// Implementation file for the sandbox2::PtraceMonitor class.
|
||||
|
||||
#include "sandboxed_api/sandbox2/monitor.h"
|
||||
#include "sandboxed_api/sandbox2/monitor_ptrace.h"
|
||||
|
||||
// clang-format off
|
||||
#include <linux/posix_types.h> // NOLINT: Needs to come before linux/ipc.h
|
||||
#include <linux/ipc.h>
|
||||
// clang-format on
|
||||
#include <sched.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
@ -48,20 +35,13 @@
|
|||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/flags/declare.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "sandboxed_api/config.h"
|
||||
#include "sandboxed_api/sandbox2/client.h"
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/sandbox2/executor.h"
|
||||
#include "sandboxed_api/sandbox2/limits.h"
|
||||
#include "sandboxed_api/sandbox2/mounts.h"
|
||||
#include "sandboxed_api/sandbox2/namespace.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/server.h"
|
||||
#include "sandboxed_api/sandbox2/policy.h"
|
||||
#include "sandboxed_api/sandbox2/regs.h"
|
||||
#include "sandboxed_api/sandbox2/result.h"
|
||||
|
@ -69,19 +49,8 @@
|
|||
#include "sandboxed_api/sandbox2/stack_trace.h"
|
||||
#include "sandboxed_api/sandbox2/syscall.h"
|
||||
#include "sandboxed_api/sandbox2/util.h"
|
||||
#include "sandboxed_api/util/file_helpers.h"
|
||||
#include "sandboxed_api/util/raw_logging.h"
|
||||
#include "sandboxed_api/util/status_macros.h"
|
||||
#include "sandboxed_api/util/strerror.h"
|
||||
#include "sandboxed_api/util/temp_file.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
ABSL_FLAG(bool, sandbox2_report_on_sandboxee_signal, true,
|
||||
"Report sandbox2 sandboxee deaths caused by signals");
|
||||
|
||||
ABSL_FLAG(bool, sandbox2_report_on_sandboxee_timeout, true,
|
||||
"Report sandbox2 sandboxee timeouts");
|
||||
|
||||
ABSL_FLAG(bool, sandbox2_log_all_stack_traces, false,
|
||||
"If set, sandbox2 monitor will log stack traces of all monitored "
|
||||
|
@ -95,7 +64,6 @@ ABSL_FLAG(absl::Duration, sandbox2_stack_traces_collection_timeout,
|
|||
|
||||
ABSL_DECLARE_FLAG(bool, sandbox2_danger_danger_permit_all);
|
||||
ABSL_DECLARE_FLAG(bool, sandbox_libunwind_crash_handler);
|
||||
ABSL_DECLARE_FLAG(string, sandbox2_danger_danger_permit_all_and_log);
|
||||
|
||||
namespace sandbox2 {
|
||||
namespace {
|
||||
|
@ -206,78 +174,14 @@ void CompleteSyscall(pid_t pid, int signo) {
|
|||
}
|
||||
}
|
||||
|
||||
void MaybeEnableTomoyoLsmWorkaround(Mounts& mounts, std::string& comms_fd_dev) {
|
||||
static auto tomoyo_active = []() -> bool {
|
||||
std::string lsm_list;
|
||||
if (auto status = sapi::file::GetContents(
|
||||
"/sys/kernel/security/lsm", &lsm_list, sapi::file::Defaults());
|
||||
!status.ok() && !absl::IsNotFound(status)) {
|
||||
VLOG(1) << "Checking active LSMs failed: " << status.message() << ": "
|
||||
<< sapi::StrError(errno);
|
||||
return false;
|
||||
}
|
||||
return absl::StrContains(lsm_list, "tomoyo");
|
||||
}();
|
||||
|
||||
if (!tomoyo_active) {
|
||||
return;
|
||||
}
|
||||
VLOG(1) << "Tomoyo LSM active, enabling workaround";
|
||||
|
||||
if (mounts.ResolvePath("/dev").ok() || mounts.ResolvePath("/dev/fd").ok()) {
|
||||
// Avoid shadowing /dev/fd/1022 below if /dev or /dev/fd is already mapped.
|
||||
VLOG(1) << "Parent dir already mapped, skipping";
|
||||
return;
|
||||
}
|
||||
|
||||
auto temp_file = sapi::CreateNamedTempFileAndClose("/tmp/");
|
||||
if (!temp_file.ok()) {
|
||||
LOG(WARNING) << "Failed to create empty temp file: " << temp_file.status();
|
||||
return;
|
||||
}
|
||||
comms_fd_dev = std::move(*temp_file);
|
||||
|
||||
// Ignore errors here, as the file itself might already be mapped.
|
||||
if (auto status = mounts.AddFileAt(
|
||||
comms_fd_dev, absl::StrCat("/dev/fd/", Comms::kSandbox2TargetExecFD),
|
||||
false);
|
||||
!status.ok()) {
|
||||
VLOG(1) << "Mapping comms FD: %s" << status.message();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MonitorBase::MonitorBase(Executor* executor, Policy* policy, Notify* notify)
|
||||
: executor_(executor),
|
||||
notify_(notify),
|
||||
policy_(policy),
|
||||
// NOLINTNEXTLINE clang-diagnostic-deprecated-declarations
|
||||
comms_(executor_->ipc()->comms()),
|
||||
ipc_(executor_->ipc()) {
|
||||
// It's a pre-connected Comms channel, no need to accept new connection.
|
||||
CHECK(comms_->IsConnected());
|
||||
std::string path =
|
||||
absl::GetFlag(FLAGS_sandbox2_danger_danger_permit_all_and_log);
|
||||
if (!path.empty()) {
|
||||
log_file_ = std::fopen(path.c_str(), "a+");
|
||||
PCHECK(log_file_ != nullptr) << "Failed to open log file '" << path << "'";
|
||||
}
|
||||
|
||||
if (auto* ns = policy_->GetNamespace(); ns) {
|
||||
// Check for the Tomoyo LSM, which is active by default in several common
|
||||
// distribution kernels (esp. Debian).
|
||||
MaybeEnableTomoyoLsmWorkaround(ns->mounts(), comms_fd_dev_);
|
||||
}
|
||||
}
|
||||
|
||||
PtraceMonitor::PtraceMonitor(Executor* executor, Policy* policy, Notify* notify)
|
||||
: MonitorBase(executor, policy, notify),
|
||||
wait_for_execve_(executor->enable_sandboxing_pre_execve_),
|
||||
uses_custom_forkserver_(executor_->fork_client_ != nullptr) {
|
||||
if (executor_->limits()->wall_time_limit() != absl::ZeroDuration()) {
|
||||
auto deadline = absl::Now() + executor_->limits()->wall_time_limit();
|
||||
LOG(INFO) << executor_->limits()->wall_time_limit();
|
||||
deadline_millis_.store(absl::ToUnixMillis(deadline),
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
|
@ -285,130 +189,6 @@ PtraceMonitor::PtraceMonitor(Executor* executor, Policy* policy, Notify* notify)
|
|||
dump_stack_request_flag_.test_and_set(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
MonitorBase::~MonitorBase() {
|
||||
if (!comms_fd_dev_.empty()) {
|
||||
std::remove(comms_fd_dev_.c_str());
|
||||
}
|
||||
if (log_file_) {
|
||||
std::fclose(log_file_);
|
||||
}
|
||||
if (network_proxy_server_) {
|
||||
network_proxy_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void LogContainer(const std::vector<std::string>& container) {
|
||||
for (size_t i = 0; i < container.size(); ++i) {
|
||||
LOG(INFO) << "[" << std::setfill('0') << std::setw(4) << i
|
||||
<< "]=" << container[i];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void MonitorBase::OnDone() {
|
||||
if (done_notification_.HasBeenNotified()) {
|
||||
return;
|
||||
}
|
||||
|
||||
notify_->EventFinished(result_);
|
||||
ipc_->InternalCleanupFdMap();
|
||||
done_notification_.Notify();
|
||||
}
|
||||
|
||||
void MonitorBase::Launch() {
|
||||
|
||||
absl::Cleanup process_cleanup = [this] {
|
||||
if (process_.init_pid > 0) {
|
||||
kill(process_.init_pid, SIGKILL);
|
||||
} else if (process_.main_pid > 0) {
|
||||
kill(process_.main_pid, SIGKILL);
|
||||
}
|
||||
};
|
||||
absl::Cleanup monitor_done = [this] { OnDone(); };
|
||||
|
||||
Namespace* ns = policy_->GetNamespace();
|
||||
if (SAPI_VLOG_IS_ON(1) && ns != nullptr) {
|
||||
std::vector<std::string> outside_entries;
|
||||
std::vector<std::string> inside_entries;
|
||||
ns->mounts().RecursivelyListMounts(
|
||||
/*outside_entries=*/&outside_entries,
|
||||
/*inside_entries=*/&inside_entries);
|
||||
VLOG(1) << "Outside entries mapped to chroot:";
|
||||
LogContainer(outside_entries);
|
||||
VLOG(1) << "Inside entries as they appear in chroot:";
|
||||
LogContainer(inside_entries);
|
||||
}
|
||||
|
||||
// Don't trace the child: it will allow to use 'strace -f' with the whole
|
||||
// sandbox master/monitor, which ptrace_attach'es to the child.
|
||||
int clone_flags = CLONE_UNTRACED;
|
||||
|
||||
if (policy_->allowed_hosts_) {
|
||||
EnableNetworkProxyServer();
|
||||
}
|
||||
|
||||
// Get PID of the sandboxee.
|
||||
bool should_have_init = ns && (ns->GetCloneFlags() & CLONE_NEWPID);
|
||||
absl::StatusOr<Executor::Process> process =
|
||||
executor_->StartSubProcess(clone_flags, ns, policy_->capabilities());
|
||||
|
||||
if (!process.ok()) {
|
||||
LOG(ERROR) << "Starting sandboxed subprocess failed: " << process.status();
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_SUBPROCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
process_ = *std::move(process);
|
||||
|
||||
if (process_.main_pid <= 0 || (should_have_init && process_.main_pid <= 0)) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_SUBPROCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!notify_->EventStarted(process_.main_pid, comms_)) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_NOTIFY);
|
||||
return;
|
||||
}
|
||||
if (!InitSendIPC()) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_IPC);
|
||||
return;
|
||||
}
|
||||
if (!InitSendCwd()) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_CWD);
|
||||
return;
|
||||
}
|
||||
if (!InitSendPolicy()) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_POLICY);
|
||||
return;
|
||||
}
|
||||
if (!WaitForSandboxReady()) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_WAIT);
|
||||
return;
|
||||
}
|
||||
if (!InitApplyLimits()) {
|
||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_LIMITS);
|
||||
return;
|
||||
}
|
||||
std::move(process_cleanup).Cancel();
|
||||
|
||||
RunInternal();
|
||||
std::move(monitor_done).Cancel();
|
||||
}
|
||||
|
||||
absl::StatusOr<Result> MonitorBase::AwaitResultWithTimeout(
|
||||
absl::Duration timeout) {
|
||||
auto done = done_notification_.WaitForNotificationWithTimeout(timeout);
|
||||
if (!done) {
|
||||
return absl::DeadlineExceededError("Sandbox did not finish within timeout");
|
||||
}
|
||||
|
||||
Join();
|
||||
return result_;
|
||||
}
|
||||
|
||||
bool PtraceMonitor::IsActivelyMonitoring() {
|
||||
// If we're still waiting for execve(), then we allow all syscalls.
|
||||
return !wait_for_execve_;
|
||||
|
@ -416,12 +196,6 @@ bool PtraceMonitor::IsActivelyMonitoring() {
|
|||
|
||||
void PtraceMonitor::SetActivelyMonitoring() { wait_for_execve_ = false; }
|
||||
|
||||
void MonitorBase::SetExitStatusCode(Result::StatusEnum final_status,
|
||||
uintptr_t reason_code) {
|
||||
CHECK(result_.final_status() == Result::UNSET);
|
||||
result_.SetExitStatusCode(final_status, reason_code);
|
||||
}
|
||||
|
||||
bool PtraceMonitor::StackTraceCollectionPossible() const {
|
||||
// Only get the stacktrace if we are not in the libunwind sandbox (avoid
|
||||
// recursion).
|
||||
|
@ -765,83 +539,6 @@ bool PtraceMonitor::InitSetupSignals() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MonitorBase::InitSendPolicy() {
|
||||
if (!policy_->SendPolicy(comms_)) {
|
||||
LOG(ERROR) << "Couldn't send policy";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MonitorBase::InitSendCwd() {
|
||||
if (!comms_->SendString(executor_->cwd_)) {
|
||||
PLOG(ERROR) << "Couldn't send cwd";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MonitorBase::InitApplyLimit(pid_t pid, int resource,
|
||||
const rlimit64& rlim) const {
|
||||
#if defined(__ANDROID__)
|
||||
using RlimitResource = int;
|
||||
#else
|
||||
using RlimitResource = __rlimit_resource;
|
||||
#endif
|
||||
|
||||
rlimit64 curr_limit;
|
||||
if (prlimit64(pid, static_cast<RlimitResource>(resource), nullptr,
|
||||
&curr_limit) == -1) {
|
||||
PLOG(ERROR) << "prlimit64(" << pid << ", " << util::GetRlimitName(resource)
|
||||
<< ")";
|
||||
} else if (rlim.rlim_cur > curr_limit.rlim_max) {
|
||||
// In such case, don't update the limits, as it will fail. Just stick to the
|
||||
// current ones (which are already lower than intended).
|
||||
LOG(ERROR) << util::GetRlimitName(resource)
|
||||
<< ": new.current > current.max (" << rlim.rlim_cur << " > "
|
||||
<< curr_limit.rlim_max << "), skipping";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prlimit64(pid, static_cast<RlimitResource>(resource), &rlim, nullptr) ==
|
||||
-1) {
|
||||
PLOG(ERROR) << "prlimit64(" << pid << ", " << util::GetRlimitName(resource)
|
||||
<< ", " << rlim.rlim_cur << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MonitorBase::InitApplyLimits() {
|
||||
Limits* limits = executor_->limits();
|
||||
return InitApplyLimit(process_.main_pid, RLIMIT_AS, limits->rlimit_as()) &&
|
||||
InitApplyLimit(process_.main_pid, RLIMIT_CPU, limits->rlimit_cpu()) &&
|
||||
InitApplyLimit(process_.main_pid, RLIMIT_FSIZE,
|
||||
limits->rlimit_fsize()) &&
|
||||
InitApplyLimit(process_.main_pid, RLIMIT_NOFILE,
|
||||
limits->rlimit_nofile()) &&
|
||||
InitApplyLimit(process_.main_pid, RLIMIT_CORE, limits->rlimit_core());
|
||||
}
|
||||
|
||||
bool MonitorBase::InitSendIPC() { return ipc_->SendFdsOverComms(); }
|
||||
|
||||
bool MonitorBase::WaitForSandboxReady() {
|
||||
uint32_t tmp;
|
||||
if (!comms_->RecvUint32(&tmp)) {
|
||||
LOG(ERROR) << "Couldn't receive 'Client::kClient2SandboxReady' message";
|
||||
return false;
|
||||
}
|
||||
if (tmp != Client::kClient2SandboxReady) {
|
||||
LOG(ERROR) << "Received " << tmp << " != Client::kClient2SandboxReady ("
|
||||
<< Client::kClient2SandboxReady << ")";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PtraceMonitor::InitPtraceAttach() {
|
||||
sanitizer::WaitForSanitizer();
|
||||
|
||||
|
@ -1035,28 +732,6 @@ void PtraceMonitor::ActionProcessSyscallViolation(
|
|||
}
|
||||
}
|
||||
|
||||
void MonitorBase::LogSyscallViolation(const Syscall& syscall) const {
|
||||
// Do not unwind libunwind.
|
||||
if (executor_->libunwind_sbox_for_pid_ != 0) {
|
||||
LOG(ERROR) << "Sandbox violation during execution of libunwind: "
|
||||
<< syscall.GetDescription();
|
||||
return;
|
||||
}
|
||||
|
||||
// So, this is an invalid syscall. Will be killed by seccomp-bpf policies as
|
||||
// well, but we should be on a safe side here as well.
|
||||
LOG(ERROR) << "SANDBOX VIOLATION : PID: " << syscall.pid() << ", PROG: '"
|
||||
<< util::GetProgName(syscall.pid())
|
||||
<< "' : " << syscall.GetDescription();
|
||||
if (SAPI_VLOG_IS_ON(1)) {
|
||||
VLOG(1) << "Cmdline: " << util::GetCmdLine(syscall.pid());
|
||||
VLOG(1) << "Task Name: " << util::GetProcStatusLine(syscall.pid(), "Name");
|
||||
VLOG(1) << "Tgid: " << util::GetProcStatusLine(syscall.pid(), "Tgid");
|
||||
}
|
||||
|
||||
LogSyscallViolationExplanation(syscall);
|
||||
}
|
||||
|
||||
void PtraceMonitor::EventPtraceSeccomp(pid_t pid, int event_msg) {
|
||||
if (event_msg < sapi::cpu::Architecture::kUnknown ||
|
||||
event_msg > sapi::cpu::Architecture::kMax) {
|
||||
|
@ -1361,44 +1036,4 @@ void PtraceMonitor::StateProcessStopped(pid_t pid, int status) {
|
|||
}
|
||||
}
|
||||
|
||||
void MonitorBase::LogSyscallViolationExplanation(const Syscall& syscall) const {
|
||||
const uintptr_t syscall_nr = syscall.nr();
|
||||
const uintptr_t arg0 = syscall.args()[0];
|
||||
|
||||
// This follows policy in Policy::GetDefaultPolicy - keep it in sync.
|
||||
if (syscall.arch() != Syscall::GetHostArch()) {
|
||||
LOG(ERROR)
|
||||
<< "This is a violation because the syscall was issued because the"
|
||||
<< " sandboxee and executor architectures are different.";
|
||||
return;
|
||||
}
|
||||
if (syscall_nr == __NR_ptrace) {
|
||||
LOG(ERROR)
|
||||
<< "This is a violation because the ptrace syscall would be unsafe in"
|
||||
<< " sandbox2, so it has been blocked.";
|
||||
return;
|
||||
}
|
||||
if (syscall_nr == __NR_bpf) {
|
||||
LOG(ERROR)
|
||||
<< "This is a violation because the bpf syscall would be risky in"
|
||||
<< " a sandbox, so it has been blocked.";
|
||||
return;
|
||||
}
|
||||
if (syscall_nr == __NR_clone && ((arg0 & CLONE_UNTRACED) != 0)) {
|
||||
LOG(ERROR) << "This is a violation because calling clone with CLONE_UNTRACE"
|
||||
<< " would be unsafe in sandbox2, so it has been blocked.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MonitorBase::EnableNetworkProxyServer() {
|
||||
int fd = ipc_->ReceiveFd(NetworkProxyClient::kFDName);
|
||||
|
||||
network_proxy_server_ = std::make_unique<NetworkProxyServer>(
|
||||
fd, &policy_->allowed_hosts_.value(), pthread_self());
|
||||
|
||||
network_proxy_thread_ = std::thread(&NetworkProxyServer::Run,
|
||||
network_proxy_server_.get());
|
||||
}
|
||||
|
||||
} // namespace sandbox2
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2019 Google LLC
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -15,16 +15,12 @@
|
|||
// The sandbox2::Monitor class is responsible for tracking the processes, and
|
||||
// displaying their current statuses (syscalls, states, violations).
|
||||
|
||||
#ifndef SANDBOXED_API_SANDBOX2_MONITOR_H_
|
||||
#define SANDBOXED_API_SANDBOX2_MONITOR_H_
|
||||
|
||||
#include <sys/resource.h>
|
||||
#ifndef SANDBOXED_API_SANDBOX2_MONITOR_PTRACE_H_
|
||||
#define SANDBOXED_API_SANDBOX2_MONITOR_PTRACE_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
|
@ -32,10 +28,8 @@
|
|||
#include "absl/status/statusor.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/sandbox2/executor.h"
|
||||
#include "sandboxed_api/sandbox2/ipc.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/server.h"
|
||||
#include "sandboxed_api/sandbox2/monitor_base.h"
|
||||
#include "sandboxed_api/sandbox2/notify.h"
|
||||
#include "sandboxed_api/sandbox2/policy.h"
|
||||
#include "sandboxed_api/sandbox2/regs.h"
|
||||
|
@ -45,103 +39,6 @@
|
|||
|
||||
namespace sandbox2 {
|
||||
|
||||
class MonitorBase {
|
||||
public:
|
||||
// executor, policy and notify are not owned by the Monitor
|
||||
MonitorBase(Executor* executor, Policy* policy, Notify* notify);
|
||||
|
||||
MonitorBase(const MonitorBase&) = delete;
|
||||
MonitorBase& operator=(const MonitorBase&) = delete;
|
||||
|
||||
virtual ~MonitorBase();
|
||||
|
||||
// Starts the Monitor.
|
||||
void Launch();
|
||||
|
||||
// Getters for private fields.
|
||||
bool IsDone() const { return done_notification_.HasBeenNotified(); }
|
||||
|
||||
// Enable network proxy server, this will start a thread in the sandbox
|
||||
// that waits for connection requests from the sandboxee.
|
||||
void EnableNetworkProxyServer();
|
||||
|
||||
pid_t pid() const { return process_.main_pid; }
|
||||
|
||||
const Result& result() const { return result_; }
|
||||
|
||||
absl::StatusOr<Result> AwaitResultWithTimeout(absl::Duration timeout);
|
||||
|
||||
virtual void Kill() = 0;
|
||||
virtual void DumpStackTrace() = 0;
|
||||
virtual void SetWallTimeLimit(absl::Duration limit) = 0;
|
||||
|
||||
protected:
|
||||
void OnDone();
|
||||
// Sets basic info status and reason code in the result object.
|
||||
void SetExitStatusCode(Result::StatusEnum final_status,
|
||||
uintptr_t reason_code);
|
||||
// Logs a SANDBOX VIOLATION message based on the registers and additional
|
||||
// explanation for the reason of the violation.
|
||||
void LogSyscallViolation(const Syscall& syscall) const;
|
||||
|
||||
// Internal objects, owned by the Sandbox2 object.
|
||||
Executor* executor_;
|
||||
Notify* notify_;
|
||||
Policy* policy_;
|
||||
// The sandboxee process.
|
||||
Executor::Process process_;
|
||||
Result result_;
|
||||
// Comms channel ptr, copied from the Executor object for convenience.
|
||||
Comms* comms_;
|
||||
// Log file specified by
|
||||
// --sandbox_danger_danger_permit_all_and_log flag.
|
||||
FILE* log_file_ = nullptr;
|
||||
// Handle to the class responsible for proxying and validating connect()
|
||||
// requests.
|
||||
std::unique_ptr<NetworkProxyServer> network_proxy_server_;
|
||||
|
||||
private:
|
||||
// Sends Policy to the Client.
|
||||
// Returns success/failure status.
|
||||
bool InitSendPolicy();
|
||||
|
||||
// Waits for the SandboxReady signal from the client.
|
||||
// Returns success/failure status.
|
||||
bool WaitForSandboxReady();
|
||||
|
||||
// Sends information about data exchange channels.
|
||||
bool InitSendIPC();
|
||||
|
||||
// Sends information about the current working directory.
|
||||
bool InitSendCwd();
|
||||
|
||||
// Applies limits on the sandboxee.
|
||||
bool InitApplyLimits();
|
||||
|
||||
// Applies individual limit on the sandboxee.
|
||||
bool InitApplyLimit(pid_t pid, int resource, const rlimit64& rlim) const;
|
||||
|
||||
// Logs an additional explanation for the possible reason of the violation
|
||||
// based on the registers.
|
||||
void LogSyscallViolationExplanation(const Syscall& syscall) const;
|
||||
|
||||
virtual void RunInternal() = 0;
|
||||
virtual void Join() = 0;
|
||||
|
||||
// IPC ptr, used for exchanging data with the sandboxee.
|
||||
IPC* ipc_;
|
||||
|
||||
// The field indicates whether the sandboxing task has been completed (either
|
||||
// successfully or with error).
|
||||
absl::Notification done_notification_;
|
||||
|
||||
// Empty temp file used for mapping the comms fd when the Tomoyo LSM is
|
||||
// active.
|
||||
std::string comms_fd_dev_;
|
||||
|
||||
std::thread network_proxy_thread_;
|
||||
};
|
||||
|
||||
class PtraceMonitor : public MonitorBase {
|
||||
public:
|
||||
PtraceMonitor(Executor* executor, Policy* policy, Notify* notify);
|
||||
|
@ -281,4 +178,4 @@ class PtraceMonitor : public MonitorBase {
|
|||
|
||||
} // namespace sandbox2
|
||||
|
||||
#endif // SANDBOXED_API_SANDBOX2_MONITOR_H_
|
||||
#endif // SANDBOXED_API_SANDBOX2_MONITOR_BASE_H_
|
|
@ -23,7 +23,7 @@
|
|||
#include "absl/log/check.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "sandboxed_api/sandbox2/monitor.h"
|
||||
#include "sandboxed_api/sandbox2/monitor_ptrace.h"
|
||||
#include "sandboxed_api/sandbox2/result.h"
|
||||
|
||||
namespace sandbox2 {
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/sandbox2/executor.h"
|
||||
#include "sandboxed_api/sandbox2/ipc.h"
|
||||
#include "sandboxed_api/sandbox2/monitor.h"
|
||||
#include "sandboxed_api/sandbox2/monitor_base.h"
|
||||
#include "sandboxed_api/sandbox2/notify.h"
|
||||
#include "sandboxed_api/sandbox2/policy.h"
|
||||
#include "sandboxed_api/sandbox2/result.h"
|
||||
|
|
Loading…
Reference in New Issue
Block a user