blob: a19b695b606e6228db433c0cb65a1ab231dc10bd [file] [log] [blame]
Garrick Evans64a2df32018-12-12 16:53:46 +09001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Garrick Evans3388a032020-03-24 11:25:55 +09005#include "patchpanel/minijailed_process_runner.h"
Garrick Evans64a2df32018-12-12 16:53:46 +09006
7#include <linux/capability.h>
Jie Jiangcf5ce9c2020-07-14 17:22:03 +09008#include <sys/wait.h>
9#include <utility>
Garrick Evans64a2df32018-12-12 16:53:46 +090010
Qijiang Fan713061e2021-03-08 15:45:12 +090011#include <base/check.h>
Jie Jiang0137f5d2020-12-16 16:23:59 +090012#include <base/files/scoped_file.h>
Garrick Evans64a2df32018-12-12 16:53:46 +090013#include <base/logging.h>
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090014#include <base/posix/eintr_wrapper.h>
Garrick Evans2470caa2020-03-04 14:15:41 +090015#include <base/strings/string_number_conversions.h>
Garrick Evans64a2df32018-12-12 16:53:46 +090016#include <base/strings/string_util.h>
Simon Glass2b1da092020-05-21 12:24:16 -060017#include <brillo/process/process.h>
Garrick Evans64a2df32018-12-12 16:53:46 +090018
Garrick Evans3388a032020-03-24 11:25:55 +090019#include "patchpanel/net_util.h"
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090020
Garrick Evans3388a032020-03-24 11:25:55 +090021namespace patchpanel {
Garrick Evans64a2df32018-12-12 16:53:46 +090022
23namespace {
24
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090025constexpr char kUnprivilegedUser[] = "nobody";
Garrick Evans6776b502020-05-01 10:41:56 +090026constexpr char kNetworkUnprivilegedUser[] = "patchpaneld";
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090027constexpr uint64_t kModprobeCapMask = CAP_TO_MASK(CAP_SYS_MODULE);
Garrick Evans8e8e3472020-01-23 14:03:50 +090028constexpr uint64_t kNetRawCapMask = CAP_TO_MASK(CAP_NET_RAW);
29constexpr uint64_t kNetRawAdminCapMask =
30 CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_RAW);
31
32// These match what is used in iptables.cc in firewalld.
Garrick Evans8e8e3472020-01-23 14:03:50 +090033constexpr char kIpPath[] = "/bin/ip";
34constexpr char kIptablesPath[] = "/sbin/iptables";
35constexpr char kIp6tablesPath[] = "/sbin/ip6tables";
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090036constexpr char kModprobePath[] = "/sbin/modprobe";
Garrick Evans6d227b92019-12-03 16:11:29 +090037constexpr char kSysctlPath[] = "/usr/sbin/sysctl";
Garrick Evans64a2df32018-12-12 16:53:46 +090038
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090039// An empty string will be returned if read fails.
Jie Jiang0137f5d2020-12-16 16:23:59 +090040std::string ReadBlockingFDToStringAndClose(base::ScopedFD fd) {
41 if (!fd.is_valid()) {
42 LOG(ERROR) << "Invalid fd";
43 return "";
44 }
45
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090046 static constexpr int kBufSize = 2048;
47 char buf[kBufSize] = {0};
48 std::string output;
49 while (true) {
Jie Jiang0137f5d2020-12-16 16:23:59 +090050 ssize_t cnt = HANDLE_EINTR(read(fd.get(), buf, kBufSize));
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090051 if (cnt == -1) {
52 PLOG(ERROR) << __func__ << " failed";
Jie Jiang0137f5d2020-12-16 16:23:59 +090053 return "";
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090054 }
55
Jie Jiang0137f5d2020-12-16 16:23:59 +090056 if (cnt == 0) {
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090057 return output;
Jie Jiang0137f5d2020-12-16 16:23:59 +090058 }
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090059
60 output.append({buf, static_cast<size_t>(cnt)});
61 }
62}
63
64} // namespace
65
66pid_t MinijailedProcessRunner::SyscallImpl::WaitPID(pid_t pid,
67 int* wstatus,
68 int options) {
69 return waitpid(pid, wstatus, options);
70}
71
72int MinijailedProcessRunner::RunSyncDestroy(
73 const std::vector<std::string>& argv,
74 brillo::Minijail* mj,
75 minijail* jail,
76 bool log_failures,
Jie Jiang0137f5d2020-12-16 16:23:59 +090077 std::string* output) {
Garrick Evans64a2df32018-12-12 16:53:46 +090078 std::vector<char*> args;
79 for (const auto& arg : argv) {
80 args.push_back(const_cast<char*>(arg.c_str()));
81 }
82 args.push_back(nullptr);
83
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090084 pid_t pid;
Jie Jiang0137f5d2020-12-16 16:23:59 +090085 int fd_stdout = -1;
86 int* stdout_p = output ? &fd_stdout : nullptr;
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090087 bool ran = mj->RunPipesAndDestroy(jail, args, &pid, nullptr /*stdin*/,
Jie Jiang0137f5d2020-12-16 16:23:59 +090088 stdout_p, nullptr /*stderr*/);
89 if (output) {
90 *output = ReadBlockingFDToStringAndClose(base::ScopedFD(fd_stdout));
91 }
92
93 int status = 0;
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090094 if (ran) {
95 ran = syscall_->WaitPID(pid, &status) == pid;
96 }
97
Garrick Evans64a2df32018-12-12 16:53:46 +090098 if (!ran) {
99 LOG(ERROR) << "Could not execute '" << base::JoinString(argv, " ") << "'";
100 } else if (log_failures && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
101 if (WIFEXITED(status)) {
Hugo Benichid4b19562019-06-21 13:00:12 +0900102 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
103 << "' exited with code " << WEXITSTATUS(status);
Garrick Evans64a2df32018-12-12 16:53:46 +0900104 } else if (WIFSIGNALED(status)) {
Hugo Benichid4b19562019-06-21 13:00:12 +0900105 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
106 << "' exited with signal " << WTERMSIG(status);
Garrick Evans64a2df32018-12-12 16:53:46 +0900107 } else {
Hugo Benichid4b19562019-06-21 13:00:12 +0900108 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
109 << "' exited with unknown status " << status;
Garrick Evans64a2df32018-12-12 16:53:46 +0900110 }
111 }
112 return ran && WIFEXITED(status) ? WEXITSTATUS(status) : -1;
113}
114
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900115int MinijailedProcessRunner::RunSync(const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900116 bool log_failures,
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900117 std::string* output) {
Jie Jiang0137f5d2020-12-16 16:23:59 +0900118 return RunSyncDestroy(argv, mj_, mj_->New(), log_failures, output);
Garrick Evans721eee52019-01-28 16:26:23 +0900119}
120
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900121void EnterChildProcessJail() {
122 brillo::Minijail* m = brillo::Minijail::GetInstance();
123 struct minijail* jail = m->New();
124
125 // Most of these return void, but DropRoot() can fail if the user/group
126 // does not exist.
127 CHECK(m->DropRoot(jail, kNetworkUnprivilegedUser, kNetworkUnprivilegedUser))
128 << "Could not drop root privileges";
Garrick Evans8e8e3472020-01-23 14:03:50 +0900129 m->UseCapabilities(jail, kNetRawCapMask);
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900130 m->Enter(jail);
131 m->Destroy(jail);
132}
133
Garrick Evans64a2df32018-12-12 16:53:46 +0900134MinijailedProcessRunner::MinijailedProcessRunner(brillo::Minijail* mj) {
135 mj_ = mj ? mj : brillo::Minijail::GetInstance();
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900136 syscall_ = std::make_unique<SyscallImpl>();
Garrick Evans64a2df32018-12-12 16:53:46 +0900137}
138
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900139MinijailedProcessRunner::MinijailedProcessRunner(
140 brillo::Minijail* mj, std::unique_ptr<SyscallImpl> syscall)
141 : mj_(mj), syscall_(std::move(syscall)) {}
142
Garrick Evans64a2df32018-12-12 16:53:46 +0900143int MinijailedProcessRunner::Run(const std::vector<std::string>& argv,
144 bool log_failures) {
145 minijail* jail = mj_->New();
146 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
Garrick Evans8e8e3472020-01-23 14:03:50 +0900147 mj_->UseCapabilities(jail, kNetRawAdminCapMask);
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900148 return RunSyncDestroy(argv, mj_, jail, log_failures, nullptr);
Garrick Evans64a2df32018-12-12 16:53:46 +0900149}
150
Garrick Evans8e8e3472020-01-23 14:03:50 +0900151int MinijailedProcessRunner::ip(const std::string& obj,
152 const std::string& cmd,
153 const std::vector<std::string>& argv,
154 bool log_failures) {
155 std::vector<std::string> args = {kIpPath, obj, cmd};
156 args.insert(args.end(), argv.begin(), argv.end());
157 return Run(args, log_failures);
158}
159
160int MinijailedProcessRunner::ip6(const std::string& obj,
161 const std::string& cmd,
162 const std::vector<std::string>& argv,
163 bool log_failures) {
164 std::vector<std::string> args = {kIpPath, "-6", obj, cmd};
165 args.insert(args.end(), argv.begin(), argv.end());
166 return Run(args, log_failures);
167}
168
169int MinijailedProcessRunner::iptables(const std::string& table,
170 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900171 bool log_failures,
172 std::string* output) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900173 std::vector<std::string> args = {kIptablesPath, "-t", table};
174 args.insert(args.end(), argv.begin(), argv.end());
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900175 return RunSync(args, log_failures, output);
Garrick Evans8e8e3472020-01-23 14:03:50 +0900176}
177
178int MinijailedProcessRunner::ip6tables(const std::string& table,
179 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900180 bool log_failures,
181 std::string* output) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900182 std::vector<std::string> args = {kIp6tablesPath, "-t", table};
183 args.insert(args.end(), argv.begin(), argv.end());
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900184 return RunSync(args, log_failures, output);
Garrick Evans8e8e3472020-01-23 14:03:50 +0900185}
186
187int MinijailedProcessRunner::modprobe_all(
188 const std::vector<std::string>& modules, bool log_failures) {
Garrick Evans78b414e2019-03-14 15:58:56 +0900189 minijail* jail = mj_->New();
190 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
191 mj_->UseCapabilities(jail, kModprobeCapMask);
192 std::vector<std::string> args = {kModprobePath, "-a"};
193 args.insert(args.end(), modules.begin(), modules.end());
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900194 return RunSyncDestroy(args, mj_, jail, log_failures, nullptr);
Garrick Evans78b414e2019-03-14 15:58:56 +0900195}
196
Garrick Evans8e8e3472020-01-23 14:03:50 +0900197int MinijailedProcessRunner::sysctl_w(const std::string& key,
198 const std::string& value,
199 bool log_failures) {
Garrick Evans6d227b92019-12-03 16:11:29 +0900200 std::vector<std::string> args = {kSysctlPath, "-w", key + "=" + value};
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900201 return RunSync(args, log_failures, nullptr);
Garrick Evans6d227b92019-12-03 16:11:29 +0900202}
203
Hugo Benichi33860d72020-07-09 16:34:01 +0900204int MinijailedProcessRunner::ip_netns_attach(const std::string& netns_name,
205 pid_t netns_pid,
206 bool log_failures) {
207 std::vector<std::string> args = {kIpPath, "netns", "attach", netns_name,
208 std::to_string(netns_pid)};
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900209 return RunSync(args, log_failures, nullptr);
Hugo Benichi33860d72020-07-09 16:34:01 +0900210}
211
212int MinijailedProcessRunner::ip_netns_delete(const std::string& netns_name,
213 bool log_failures) {
214 std::vector<std::string> args = {kIpPath, "netns", "delete", netns_name};
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900215 return RunSync(args, log_failures, nullptr);
Hugo Benichi33860d72020-07-09 16:34:01 +0900216}
217
Garrick Evans3388a032020-03-24 11:25:55 +0900218} // namespace patchpanel