blob: e50003f28d42dba2c61e84bd55b858b837062bad [file] [log] [blame]
// Copyright 2019 The Chromium OS 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 "patchpanel/minijailed_process_runner.h"
#include <linux/capability.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <brillo/process.h>
#include "patchpanel/net_util.h"
namespace patchpanel {
namespace {
constexpr char kUnprivilegedUser[] = "nobody";
constexpr char kNetworkUnprivilegedUser[] = "patchpaneld";
constexpr char kChownCapMask = CAP_TO_MASK(CAP_CHOWN);
constexpr uint64_t kModprobeCapMask = CAP_TO_MASK(CAP_SYS_MODULE);
constexpr uint64_t kNetRawCapMask = CAP_TO_MASK(CAP_NET_RAW);
constexpr uint64_t kNetRawAdminCapMask =
CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_RAW);
// These match what is used in iptables.cc in firewalld.
constexpr char kBrctlPath[] = "/sbin/brctl";
constexpr char kChownPath[] = "/bin/chown";
constexpr char kIpPath[] = "/bin/ip";
constexpr char kIptablesPath[] = "/sbin/iptables";
constexpr char kIp6tablesPath[] = "/sbin/ip6tables";
constexpr char kModprobePath[] = "/sbin/modprobe";
constexpr char kNsEnterPath[] = "/usr/bin/nsenter";
constexpr char kSysctlPath[] = "/usr/sbin/sysctl";
constexpr char kSentinelFile[] = "/dev/.arc_network_ready";
constexpr char kTouchPath[] = "/system/bin/touch";
int RunSyncDestroy(const std::vector<std::string>& argv,
brillo::Minijail* mj,
minijail* jail,
bool log_failures) {
std::vector<char*> args;
for (const auto& arg : argv) {
args.push_back(const_cast<char*>(arg.c_str()));
}
args.push_back(nullptr);
int status;
bool ran = mj->RunSyncAndDestroy(jail, args, &status);
if (!ran) {
LOG(ERROR) << "Could not execute '" << base::JoinString(argv, " ") << "'";
} else if (log_failures && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
if (WIFEXITED(status)) {
LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
<< "' exited with code " << WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
<< "' exited with signal " << WTERMSIG(status);
} else {
LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
<< "' exited with unknown status " << status;
}
}
return ran && WIFEXITED(status) ? WEXITSTATUS(status) : -1;
}
int RunSync(const std::vector<std::string>& argv,
brillo::Minijail* mj,
bool log_failures) {
return RunSyncDestroy(argv, mj, mj->New(), log_failures);
}
} // namespace
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, kNetworkUnprivilegedUser, kNetworkUnprivilegedUser))
<< "Could not drop root privileges";
m->UseCapabilities(jail, kNetRawCapMask);
m->Enter(jail);
m->Destroy(jail);
}
MinijailedProcessRunner::MinijailedProcessRunner(brillo::Minijail* mj) {
mj_ = mj ? mj : brillo::Minijail::GetInstance();
}
int MinijailedProcessRunner::Run(const std::vector<std::string>& argv,
bool log_failures) {
minijail* jail = mj_->New();
CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
mj_->UseCapabilities(jail, kNetRawAdminCapMask);
return RunSyncDestroy(argv, mj_, jail, log_failures);
}
int MinijailedProcessRunner::RestoreDefaultNamespace(const std::string& ifname,
pid_t pid) {
return RunSync({kNsEnterPath, "-t", base::NumberToString(pid), "-n", "--",
kIpPath, "link", "set", ifname, "netns", "1"},
mj_, true);
}
int MinijailedProcessRunner::WriteSentinelToContainer(pid_t pid) {
return RunSync({kNsEnterPath, "-t", base::NumberToString(pid), "--mount",
"--pid", "--", kTouchPath, kSentinelFile},
mj_, true);
}
int MinijailedProcessRunner::brctl(const std::string& cmd,
const std::vector<std::string>& argv,
bool log_failures) {
std::vector<std::string> args = {kBrctlPath, cmd};
args.insert(args.end(), argv.begin(), argv.end());
return Run(args, log_failures);
}
int MinijailedProcessRunner::chown(const std::string& uid,
const std::string& gid,
const std::string& file,
bool log_failures) {
minijail* jail = mj_->New();
CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
mj_->UseCapabilities(jail, kChownCapMask);
std::vector<std::string> args = {kChownPath, uid + ":" + gid, file};
return RunSyncDestroy(args, mj_, jail, log_failures);
}
int MinijailedProcessRunner::ip(const std::string& obj,
const std::string& cmd,
const std::vector<std::string>& argv,
bool log_failures) {
std::vector<std::string> args = {kIpPath, obj, cmd};
args.insert(args.end(), argv.begin(), argv.end());
return Run(args, log_failures);
}
int MinijailedProcessRunner::ip6(const std::string& obj,
const std::string& cmd,
const std::vector<std::string>& argv,
bool log_failures) {
std::vector<std::string> args = {kIpPath, "-6", obj, cmd};
args.insert(args.end(), argv.begin(), argv.end());
return Run(args, log_failures);
}
int MinijailedProcessRunner::iptables(const std::string& table,
const std::vector<std::string>& argv,
bool log_failures) {
std::vector<std::string> args = {kIptablesPath, "-t", table};
args.insert(args.end(), argv.begin(), argv.end());
return RunSync(args, mj_, log_failures);
}
int MinijailedProcessRunner::ip6tables(const std::string& table,
const std::vector<std::string>& argv,
bool log_failures) {
std::vector<std::string> args = {kIp6tablesPath, "-t", table};
args.insert(args.end(), argv.begin(), argv.end());
return RunSync(args, mj_, log_failures);
}
int MinijailedProcessRunner::modprobe_all(
const std::vector<std::string>& modules, bool log_failures) {
minijail* jail = mj_->New();
CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
mj_->UseCapabilities(jail, kModprobeCapMask);
std::vector<std::string> args = {kModprobePath, "-a"};
args.insert(args.end(), modules.begin(), modules.end());
return RunSyncDestroy(args, mj_, jail, log_failures);
}
int MinijailedProcessRunner::sysctl_w(const std::string& key,
const std::string& value,
bool log_failures) {
std::vector<std::string> args = {kSysctlPath, "-w", key + "=" + value};
return RunSync(args, mj_, log_failures);
}
} // namespace patchpanel