blob: dab0d19bb1a45019d55a2f7b718bb4f7af408b37 [file] [log] [blame]
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "patchpanel/minijailed_process_runner.h"
#include <linux/capability.h>
#include <linux/filter.h>
#include <poll.h>
#include <sys/mman.h>
#include <unistd.h>
#include <algorithm>
#include <csignal>
#include <string_view>
#include <utility>
#include <base/check.h>
#include <base/containers/contains.h>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/logging.h>
#include <base/memory/ptr_util.h>
#include <base/posix/eintr_wrapper.h>
#include <base/strings/strcat.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <brillo/process/process.h>
namespace patchpanel {
namespace {
constexpr char kUnprivilegedUser[] = "nobody";
constexpr uint64_t kModprobeCapMask = CAP_TO_MASK(CAP_SYS_MODULE);
constexpr uint64_t kNetRawCapMask = CAP_TO_MASK(CAP_NET_RAW);
constexpr uint64_t kNetAdminCapMask = CAP_TO_MASK(CAP_NET_ADMIN);
constexpr uint64_t kNetRawAdminCapMask =
CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_RAW);
// - 39 for CAP_BPF. This does not exist on all kernels so we need to define it
// here.
// - CAP_TO_MASK() only works for a CAP whose index is less than 32.
//
// TODO(b/311100871): Switch to use CAP_BPF after all kernels are 5.8+.
constexpr uint64_t kBPFCapMask = 1ull << 39;
// `ip netns` needs CAP_SYS_ADMIN for mount(), and CAP_SYS_PTRACE for accessing
// `/proc/${pid}/ns/net` of other processes.
constexpr uint64_t kIpNetnsCapMask =
CAP_TO_MASK(CAP_SYS_PTRACE) | CAP_TO_MASK(CAP_SYS_ADMIN);
// Size for buffer used for args type conversion.
constexpr uint16_t kArgsBufferSize = 4096;
// These match what is used in iptables.cc in firewalld.
constexpr char kIpPath[] = "/bin/ip";
constexpr char kIptablesPath[] = "/sbin/iptables";
constexpr char kIp6tablesPath[] = "/sbin/ip6tables";
constexpr std::string_view kIptablesRestorePath = "/sbin/iptables-restore";
constexpr std::string_view kIp6tablesRestorePath = "/sbin/ip6tables-restore";
constexpr char kModprobePath[] = "/usr/bin/modprobe";
constexpr char kConntrackPath[] = "/usr/sbin/conntrack";
constexpr char kIptablesSeccompFilterPath[] =
"/usr/share/policy/iptables-seccomp.bpf.policy";
base::LazyInstance<MinijailedProcessRunner>::DestructorAtExit
g_process_runner_ = LAZY_INSTANCE_INITIALIZER;
// Does some simple check for whether |token| can be fed to iptables. The main
// purpose is to avoid that one token can be interpreted as two, or multiple
// tokens can be interpreted as one.
bool IsValidTokenForIptables(std::string_view token) {
const auto is_invalid_char = [](char c) {
return std::isspace(c) || c == '\'' || c == '"';
};
return std::find_if(token.begin(), token.end(), is_invalid_char) ==
token.end();
}
// The implementation logic is copied from
// src/platform/minijail/minijail0_cli.c:read_seccomp_filter().
bool LoadSeccompFilter(const base::FilePath& policy_bpf_file,
std::vector<struct sock_filter>* output_data,
struct sock_fprog* output_sock_fprog) {
base::ScopedFILE f(fopen(policy_bpf_file.value().c_str(), "re"));
if (!f) {
PLOG(ERROR) << "Failed to open " << policy_bpf_file;
return false;
}
off_t file_size = -1;
if (fseeko(f.get(), 0, SEEK_END) == -1 ||
(file_size = ftello(f.get())) == -1) {
PLOG(ERROR) << "Failed to get size of " << policy_bpf_file;
return false;
}
if (file_size % int{sizeof(struct sock_filter)} != 0) {
LOG(ERROR) << "The policy file " << policy_bpf_file
<< " has an invalid size " << file_size;
return false;
}
rewind(f.get());
auto filter_size =
static_cast<size_t>(file_size) / sizeof(struct sock_filter);
output_data->resize(filter_size);
if (fread(output_data->data(), sizeof(struct sock_filter), filter_size,
f.get()) != filter_size) {
PLOG(ERROR) << "Failed to read " << policy_bpf_file;
return false;
}
output_sock_fprog->len = static_cast<uint16_t>(output_data->size());
output_sock_fprog->filter = output_data->data();
return true;
}
} // namespace
std::vector<char*> MinijailedProcessRunner::StringViewToCstrings(
base::span<std::string_view> argv, base::span<char> buffer) {
std::vector<char*> results;
results.reserve(argv.size() + 1);
for (auto arg : argv) {
// Check if argument size exceeds remaining buffer size.
if (arg.size() + 1 > buffer.size()) {
LOG(ERROR) << "Buffer size is not enough";
return {};
}
char* cur = buffer.data();
std::memcpy(cur, arg.data(), arg.size());
// Copied string_view data is not null terminated, add null-terminator
// manually.
cur[arg.size()] = '\0';
results.push_back(cur);
// Advance span by number of bytes of argument and null-terminator.
buffer = buffer.subspan(arg.size() + 1);
}
results.push_back(nullptr);
return results;
}
int MinijailedProcessRunner::RunSyncDestroy(base::span<std::string_view> argv,
brillo::Minijail* mj,
minijail* jail,
bool log_failures,
std::string* output) {
const base::TimeTicks started_at = base::TimeTicks::Now();
const std::string logging_tag =
base::StrCat({"'", base::JoinString(argv, " "), "'"});
// Assign a buffer large enough to store all the arguments passed in.
static char buffer[kArgsBufferSize];
base::span<char> memory(buffer, kArgsBufferSize);
std::vector<char*> args = StringViewToCstrings(argv, memory);
if (args.empty()) {
LOG(DFATAL) << "Failed to convert arguments to Cstrings for "
<< logging_tag;
return -1;
}
// Helper function to redirect a child fd to an anonymous file in memory.
// `name` will only be used for logging and debugging purposes, and can be
// same for different fds. Note that the created anonymous file will be
// removed automatically after there is no reference on it.
auto redirect_child_fd =
[&jail, &logging_tag](int child_fd, const char* name) -> base::ScopedFD {
base::ScopedFD memfd(memfd_create(name, /*flags=*/0));
if (!memfd.is_valid()) {
PLOG(ERROR) << "Failed to create memfd of " << name << " for "
<< logging_tag;
return {};
}
if (minijail_preserve_fd(jail, memfd.get(), child_fd) != 0) {
PLOG(ERROR) << "Failed to preserve fd of " << name << " for "
<< logging_tag;
return {};
}
return memfd;
};
// Helper function to read from the file created by `redirect_child_fd`.
auto read_memfd_to_string = [&logging_tag](int fd, std::string* out) {
auto path = base::StringPrintf("/proc/self/fd/%d", fd);
if (!base::ReadFileToString(base::FilePath(path), out)) {
PLOG(ERROR) << "Failed to read " << path << " for " << logging_tag;
}
};
base::ScopedFD stdout_fd, stderr_fd;
if (output) {
stdout_fd = redirect_child_fd(STDOUT_FILENO, "stdout");
if (!stdout_fd.is_valid()) {
return -1;
}
}
if (log_failures) {
stderr_fd = redirect_child_fd(STDERR_FILENO, "stderr");
if (!stderr_fd.is_valid()) {
return -1;
}
}
pid_t pid;
if (!mj->RunAndDestroy(jail, args, &pid)) {
LOG(ERROR) << "Could not execute " << logging_tag;
return -1;
}
int status = 0;
if (system_->WaitPid(pid, &status) == -1) {
LOG(ERROR) << "Failed to waitpid() for " << logging_tag;
return -1;
}
const base::TimeDelta duration = base::TimeTicks::Now() - started_at;
if (duration > base::Seconds(1)) {
LOG(WARNING) << logging_tag << " took " << duration.InMilliseconds()
<< "ms to finish.";
}
if (stdout_fd.is_valid()) {
read_memfd_to_string(stdout_fd.get(), output);
}
if (log_failures && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
if (WIFEXITED(status)) {
LOG(WARNING) << logging_tag << " exited with code "
<< WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
LOG(WARNING) << logging_tag << " exited with signal " << WTERMSIG(status);
} else {
LOG(WARNING) << logging_tag << " exited with unknown status " << status;
}
std::string stderr_buf;
read_memfd_to_string(stderr_fd.get(), &stderr_buf);
base::TrimWhitespaceASCII(stderr_buf, base::TRIM_TRAILING, &stderr_buf);
if (!stderr_buf.empty()) {
LOG(WARNING) << "stderr: " << stderr_buf;
}
}
return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
}
void EnterChildProcessJail() {
brillo::Minijail* m = brillo::Minijail::GetInstance();
struct minijail* jail = m->New();
// Most of these return void, but DropRoot() can fail if the user/group
// does not exist.
CHECK(m->DropRoot(jail, kPatchpaneldUser, kPatchpaneldGroup))
<< "Could not drop root privileges";
m->UseCapabilities(jail, kNetRawCapMask);
m->Enter(jail);
m->Destroy(jail);
}
void EnterChildProcessJailWithNetAdmin() {
brillo::Minijail* m = brillo::Minijail::GetInstance();
struct minijail* jail = m->New();
// Most of these return void, but DropRoot() can fail if the user/group
// does not exist.
CHECK(m->DropRoot(jail, kPatchpaneldUser, kPatchpaneldGroup))
<< "Could not drop root privileges";
m->UseCapabilities(jail, kNetAdminCapMask);
m->Enter(jail);
m->Destroy(jail);
}
MinijailedProcessRunner* MinijailedProcessRunner::GetInstance() {
return g_process_runner_.Pointer();
}
MinijailedProcessRunner::MinijailedProcessRunner()
: mj_(brillo::Minijail::GetInstance()), system_(new System()) {}
MinijailedProcessRunner::MinijailedProcessRunner(brillo::Minijail* mj,
std::unique_ptr<System> system)
: mj_(mj), system_(std::move(system)) {}
int MinijailedProcessRunner::RunIp(base::span<std::string_view> argv,
bool as_patchpanel_user,
bool log_failures) {
minijail* jail = mj_->New();
if (as_patchpanel_user) {
CHECK(mj_->DropRoot(jail, kPatchpaneldUser, kPatchpaneldGroup));
minijail_inherit_usergroups(jail);
} else {
CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
}
mj_->UseCapabilities(jail, kNetRawAdminCapMask);
return RunSyncDestroy(argv, mj_, jail, log_failures, nullptr);
}
int MinijailedProcessRunner::ip(std::string_view obj,
std::string_view cmd,
base::span<const std::string> argv,
bool as_patchpanel_user,
bool log_failures) {
std::vector<std::string_view> args = {kIpPath, obj, cmd};
args.insert(args.end(), argv.begin(), argv.end());
return RunIp(args, as_patchpanel_user, log_failures);
}
int MinijailedProcessRunner::ip(std::string_view obj,
std::string_view cmd,
base::span<std::string_view> argv,
bool as_patchpanel_user,
bool log_failures) {
std::vector<std::string_view> args = {kIpPath, obj, cmd};
args.insert(args.end(), argv.begin(), argv.end());
return RunIp(args, as_patchpanel_user, log_failures);
}
int MinijailedProcessRunner::ip(std::string_view obj,
std::string_view cmd,
std::initializer_list<std::string_view> argv,
bool as_patchpanel_user,
bool log_failures) {
std::vector<std::string_view> args = {kIpPath, obj, cmd};
args.insert(args.end(), argv.begin(), argv.end());
return RunIp(args, as_patchpanel_user, log_failures);
}
int MinijailedProcessRunner::ip6(std::string_view obj,
std::string_view cmd,
base::span<const std::string> argv,
bool as_patchpanel_user,
bool log_failures) {
std::vector<std::string_view> args = {kIpPath, "-6", obj, cmd};
args.insert(args.end(), argv.begin(), argv.end());
return RunIp(args, as_patchpanel_user, log_failures);
}
int MinijailedProcessRunner::ip6(std::string_view obj,
std::string_view cmd,
base::span<std::string_view> argv,
bool as_patchpanel_user,
bool log_failures) {
std::vector<std::string_view> args = {kIpPath, "-6", obj, cmd};
args.insert(args.end(), argv.begin(), argv.end());
return RunIp(args, as_patchpanel_user, log_failures);
}
int MinijailedProcessRunner::ip6(std::string_view obj,
std::string_view cmd,
std::initializer_list<std::string_view> argv,
bool as_patchpanel_user,
bool log_failures) {
std::vector<std::string_view> args = {kIpPath, "-6", obj, cmd};
args.insert(args.end(), argv.begin(), argv.end());
return RunIp(args, as_patchpanel_user, log_failures);
}
int MinijailedProcessRunner::iptables(Iptables::Table table,
Iptables::Command command,
std::string_view chain,
base::span<const std::string> argv,
bool log_failures,
std::string* output) {
std::vector<std::string_view> args;
args.insert(args.end(), argv.begin(), argv.end());
return iptables(table, command, chain, args, log_failures, output);
}
int MinijailedProcessRunner::iptables(Iptables::Table table,
Iptables::Command command,
std::string_view chain,
base::span<std::string_view> argv,
bool log_failures,
std::string* output) {
if (iptables_batch_mode_) {
CHECK_EQ(output, nullptr);
bool success = AppendPendingIptablesRule(table, command, chain, argv,
&pending_iptables_rules_);
return success ? 0 : -1;
}
return RunIptables(kIptablesPath, table, command, chain, argv, log_failures,
output);
}
int MinijailedProcessRunner::iptables(
Iptables::Table table,
Iptables::Command command,
std::string_view chain,
std::initializer_list<std::string_view> argv,
bool log_failures,
std::string* output) {
std::vector<std::string_view> args(argv);
return iptables(table, command, chain, args, log_failures, output);
}
int MinijailedProcessRunner::ip6tables(Iptables::Table table,
Iptables::Command command,
std::string_view chain,
base::span<const std::string> argv,
bool log_failures,
std::string* output) {
std::vector<std::string_view> args;
args.insert(args.end(), argv.begin(), argv.end());
return ip6tables(table, command, chain, args, log_failures, output);
}
int MinijailedProcessRunner::ip6tables(Iptables::Table table,
Iptables::Command command,
std::string_view chain,
base::span<std::string_view> argv,
bool log_failures,
std::string* output) {
if (iptables_batch_mode_) {
CHECK_EQ(output, nullptr);
bool success = AppendPendingIptablesRule(table, command, chain, argv,
&pending_ip6tables_rules_);
return success ? 0 : -1;
}
return RunIptables(kIp6tablesPath, table, command, chain, argv, log_failures,
output);
}
int MinijailedProcessRunner::ip6tables(
Iptables::Table table,
Iptables::Command command,
std::string_view chain,
std::initializer_list<std::string_view> argv,
bool log_failures,
std::string* output) {
std::vector<std::string_view> args(argv);
return ip6tables(table, command, chain, args, log_failures, output);
}
int MinijailedProcessRunner::RunIptables(std::string_view iptables_path,
Iptables::Table table,
Iptables::Command command,
std::string_view chain,
base::span<std::string_view> argv,
bool log_failures,
std::string* output) {
auto table_name = Iptables::TableName(table);
auto command_name = Iptables::CommandName(command);
std::vector<std::string_view> args = {iptables_path, "-t", table_name,
command_name};
// TODO(b/278486416): Datapath::DumpIptables() needs support for passing an
// empty chain. However, we cannot pass an empty argument to iptables
// directly, so |chain| must be skipped in that case. Remove this temporary
// work-around once chains are passed with an enum or a better data type.
if (!chain.empty()) {
args.push_back(chain);
}
args.insert(args.end(), argv.begin(), argv.end());
minijail* jail = mj_->New();
// TODO(b/311100871): Only add CAP_BPF for iptables commands required that but
// not all.
mj_->UseCapabilities(jail, kNetRawAdminCapMask | kBPFCapMask);
UseIptablesSeccompFilter(jail);
return RunSyncDestroy(args, mj_, jail, log_failures, output);
}
int MinijailedProcessRunner::modprobe_all(base::span<const std::string> modules,
bool log_failures) {
minijail* jail = mj_->New();
CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
mj_->UseCapabilities(jail, kModprobeCapMask);
std::vector<std::string_view> args = {kModprobePath, "-a"};
args.insert(args.end(), modules.begin(), modules.end());
return RunSyncDestroy(args, mj_, jail, log_failures, nullptr);
}
int MinijailedProcessRunner::ip_netns_add(std::string_view netns_name,
bool log_failures) {
std::vector<std::string_view> args = {kIpPath, "netns", "add", netns_name};
return RunIpNetns(args, log_failures);
}
int MinijailedProcessRunner::ip_netns_attach(std::string_view netns_name,
pid_t netns_pid,
bool log_failures) {
auto pid = std::to_string(netns_pid);
std::vector<std::string_view> args = {kIpPath, "netns", "attach", netns_name,
pid};
return RunIpNetns(args, log_failures);
}
int MinijailedProcessRunner::ip_netns_delete(std::string_view netns_name,
bool log_failures) {
std::vector<std::string_view> args = {kIpPath, "netns", "delete", netns_name};
return RunIpNetns(args, log_failures);
}
int MinijailedProcessRunner::RunIpNetns(base::span<std::string_view> argv,
bool log_failures) {
minijail* jail = mj_->New();
CHECK(mj_->DropRoot(jail, kPatchpaneldUser, kPatchpaneldGroup));
mj_->UseCapabilities(jail, kIpNetnsCapMask);
return RunSyncDestroy(argv, mj_, jail, log_failures, nullptr);
}
int MinijailedProcessRunner::conntrack(std::string_view command,
base::span<const std::string> argv,
bool log_failures) {
std::vector<std::string_view> args = {kConntrackPath, command};
args.insert(args.end(), argv.begin(), argv.end());
// TODO(b/178980202): insert a seccomp filter right from the start for
// conntrack.
minijail* jail = mj_->New();
CHECK(mj_->DropRoot(jail, kPatchpaneldUser, kPatchpaneldGroup));
mj_->UseCapabilities(jail, kNetAdminCapMask);
return RunSyncDestroy(args, mj_, jail, log_failures, nullptr);
}
int MinijailedProcessRunner::iptables_restore(std::string_view script_file,
bool log_failures) {
return RunIptablesRestore(kIptablesRestorePath, script_file, log_failures);
}
int MinijailedProcessRunner::ip6tables_restore(std::string_view script_file,
bool log_failures) {
return RunIptablesRestore(kIp6tablesRestorePath, script_file, log_failures);
}
int MinijailedProcessRunner::RunIptablesRestore(
std::string_view iptables_restore_path,
std::string_view script_file,
bool log_failures) {
std::vector<std::string_view> args = {iptables_restore_path, script_file,
"-w"};
minijail* jail = mj_->New();
mj_->UseCapabilities(jail, kNetRawAdminCapMask);
UseIptablesSeccompFilter(jail);
return RunSyncDestroy(args, mj_, jail, log_failures, nullptr);
}
using ScopedIptablesBatchMode =
MinijailedProcessRunner::ScopedIptablesBatchMode;
ScopedIptablesBatchMode::ScopedIptablesBatchMode(
MinijailedProcessRunner* runner)
: runner_(runner) {}
ScopedIptablesBatchMode::~ScopedIptablesBatchMode() {
if (runner_->iptables_batch_mode_) {
runner_->RunPendingIptablesInBatch();
}
}
std::unique_ptr<ScopedIptablesBatchMode>
MinijailedProcessRunner::AcquireIptablesBatchMode() {
if (iptables_batch_mode_) {
LOG(ERROR) << "Already in iptables batch mode";
return nullptr;
}
iptables_batch_mode_ = true;
return base::WrapUnique(new ScopedIptablesBatchMode(this));
}
bool MinijailedProcessRunner::CommitIptablesRules(
std::unique_ptr<ScopedIptablesBatchMode> batch_mode) {
CHECK(batch_mode);
return RunPendingIptablesInBatch();
}
bool MinijailedProcessRunner::AppendPendingIptablesRule(
Iptables::Table table,
Iptables::Command command,
std::string_view chain,
base::span<std::string_view> argv,
TableToRules* pending_rules) {
// A few args for calling iptables are not generated by patchpanel itself
// (e.g., some interface names). Let's do some basic check here to avoid any
// possibilities of injection when calling iptables (e.g, the input is "\n-I
// -j ACCEPT").
if (!IsValidTokenForIptables(chain)) {
LOG(ERROR) << "Invalid chain name " << chain;
return false;
}
for (const auto& arg : argv) {
if (!IsValidTokenForIptables(arg)) {
LOG(ERROR) << "Invalid input for iptables " << arg;
return false;
}
}
// TODO(b/325359902): Change the return type of Iptables::CommandName to be
// string_view and change `args` to be string_view vector as well.
std::vector<std::string> args;
using Command = Iptables::Command;
switch (command) {
case Command::kA:
case Command::kD:
case Command::kF:
case Command::kI:
case Command::kX:
args = {Iptables::CommandName(command), std::string(chain)};
for (auto a : argv) {
args.push_back(std::string(a));
}
break;
case Command::kN:
// Convert `-N chain` to `:chain - [0:0]`, which will flush the rules and
// reset counters if the chain exist, or create a new chain otherwise.
args = {base::StrCat({":", chain, " - [0:0]"})};
break;
case Command::kL:
case Command::kS:
case Command::kC:
// These commands are meaningful in iptables-restore, but do not make
// sense here.
CHECK(false);
}
// TODO(jiejiang): Remove "-w" when calling iptables()/ip6tables().
if (args.back() == "-w") {
args.pop_back();
}
// Reject rules containing "-w" in the middle. "-w" can be a valid value for
// other options (e.g., for an interface name). There is no easy way to check
// if it's for `--wait` or not, but it should not appear in our use cases.
// Having a check here instead of letting the execution of iptables fail to
// avoid the misuse of this function.
if (base::Contains(args, "-w")) {
LOG(ERROR) << "iptables rule contains `-w` unexpectedly";
return false;
}
(*pending_rules)[table].push_back(base::JoinString(args, " "));
return true;
}
bool MinijailedProcessRunner::RunPendingIptablesInBatch() {
CHECK(iptables_batch_mode_);
iptables_batch_mode_ = false;
bool success = true;
success &= RunPendingIptablesInBatchImpl(kIptablesRestorePath,
pending_iptables_rules_);
pending_iptables_rules_.clear();
success &= RunPendingIptablesInBatchImpl(kIp6tablesRestorePath,
pending_ip6tables_rules_);
pending_ip6tables_rules_.clear();
return success;
}
bool MinijailedProcessRunner::RunPendingIptablesInBatchImpl(
std::string_view iptables_restore_path,
const TableToRules& table_to_rules) {
if (table_to_rules.empty()) {
// We may have rules only for IPv4 or IPv6, so this is expected.
return true;
}
std::vector<std::string> lines;
for (const auto& [table, rules] : table_to_rules) {
lines.push_back(base::StrCat({"*", Iptables::TableName(table)}));
lines.insert(lines.end(), rules.begin(), rules.end());
// Need a "\n" after "COMMIT". Add it here since JoinString() won't do
// it for the last line.
lines.push_back("COMMIT\n");
}
std::string input = base::JoinString(lines, "\n");
// TODO(b/328151873): Write to the stdin pipe would be easier in logic but
// complicated in implementation now. Refactor this after we have a better
// Process abstraction.
base::ScopedFD script_fd(memfd_create("iptables-restore", /*flags=*/0));
if (!script_fd.is_valid()) {
PLOG(ERROR) << "Failed to create input file to iptables-restore";
return false;
}
if (!base::WriteFileDescriptor(script_fd.get(), input)) {
PLOG(ERROR) << "Failed to generate input file to iptables-restore";
return false;
}
auto script_path = base::StringPrintf("/proc/self/fd/%d", script_fd.get());
minijail* jail = mj_->New();
mj_->UseCapabilities(jail, kNetRawAdminCapMask | kBPFCapMask);
UseIptablesSeccompFilter(jail);
std::vector<std::string_view> args = {iptables_restore_path, "-n",
script_path, "-w"};
int ret = RunSyncDestroy(args, mj_, jail, /*log_failures=*/true, nullptr);
// TODO(b/328151873): Parse stderr so we can also log which line contains an
// error.
if (ret != 0) {
LOG(ERROR) << iptables_restore_path << " exited with " << ret
<< ", input: " << input;
}
return ret == 0;
}
void MinijailedProcessRunner::UseIptablesSeccompFilter(minijail* jail) {
// Read the binary seccomp filters for iptables. Crash the process on failure
// since 1) this is not expected, 2) may indicate a security issue, 3) follow
// the API design of libminijail (the following calls to libminijail will also
// incur a crash on failure).
if (iptables_seccomp_filter_data_.empty()) {
if (!LoadSeccompFilter(base::FilePath(kIptablesSeccompFilterPath),
&iptables_seccomp_filter_data_,
&iptables_seccomp_filter_)) {
LOG(FATAL) << "Failed to load seccomp filter for iptables";
}
}
minijail_no_new_privs(jail);
minijail_use_seccomp_filter(jail);
minijail_set_seccomp_filters(jail, &iptables_seccomp_filter_);
}
} // namespace patchpanel