blob: ebe64621214919dc5365f5df7fd3e22f2447041a [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
Jie Jiang0137f5d2020-12-16 16:23:59 +090011#include <base/files/scoped_file.h>
Garrick Evans64a2df32018-12-12 16:53:46 +090012#include <base/logging.h>
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090013#include <base/posix/eintr_wrapper.h>
Garrick Evans2470caa2020-03-04 14:15:41 +090014#include <base/strings/string_number_conversions.h>
Garrick Evans64a2df32018-12-12 16:53:46 +090015#include <base/strings/string_util.h>
Simon Glass2b1da092020-05-21 12:24:16 -060016#include <brillo/process/process.h>
Garrick Evans64a2df32018-12-12 16:53:46 +090017
Garrick Evans3388a032020-03-24 11:25:55 +090018#include "patchpanel/net_util.h"
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090019
Garrick Evans3388a032020-03-24 11:25:55 +090020namespace patchpanel {
Garrick Evans64a2df32018-12-12 16:53:46 +090021
22namespace {
23
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090024constexpr char kUnprivilegedUser[] = "nobody";
Garrick Evans6776b502020-05-01 10:41:56 +090025constexpr char kNetworkUnprivilegedUser[] = "patchpaneld";
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090026constexpr uint64_t kModprobeCapMask = CAP_TO_MASK(CAP_SYS_MODULE);
Garrick Evans8e8e3472020-01-23 14:03:50 +090027constexpr uint64_t kNetRawCapMask = CAP_TO_MASK(CAP_NET_RAW);
28constexpr uint64_t kNetRawAdminCapMask =
29 CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_RAW);
30
31// These match what is used in iptables.cc in firewalld.
Garrick Evans8e8e3472020-01-23 14:03:50 +090032constexpr char kIpPath[] = "/bin/ip";
33constexpr char kIptablesPath[] = "/sbin/iptables";
34constexpr char kIp6tablesPath[] = "/sbin/ip6tables";
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090035constexpr char kModprobePath[] = "/sbin/modprobe";
Garrick Evans6d227b92019-12-03 16:11:29 +090036constexpr char kSysctlPath[] = "/usr/sbin/sysctl";
Garrick Evans64a2df32018-12-12 16:53:46 +090037
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090038// An empty string will be returned if read fails.
Jie Jiang0137f5d2020-12-16 16:23:59 +090039std::string ReadBlockingFDToStringAndClose(base::ScopedFD fd) {
40 if (!fd.is_valid()) {
41 LOG(ERROR) << "Invalid fd";
42 return "";
43 }
44
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090045 static constexpr int kBufSize = 2048;
46 char buf[kBufSize] = {0};
47 std::string output;
48 while (true) {
Jie Jiang0137f5d2020-12-16 16:23:59 +090049 ssize_t cnt = HANDLE_EINTR(read(fd.get(), buf, kBufSize));
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090050 if (cnt == -1) {
51 PLOG(ERROR) << __func__ << " failed";
Jie Jiang0137f5d2020-12-16 16:23:59 +090052 return "";
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090053 }
54
Jie Jiang0137f5d2020-12-16 16:23:59 +090055 if (cnt == 0) {
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090056 return output;
Jie Jiang0137f5d2020-12-16 16:23:59 +090057 }
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090058
59 output.append({buf, static_cast<size_t>(cnt)});
60 }
61}
62
63} // namespace
64
65pid_t MinijailedProcessRunner::SyscallImpl::WaitPID(pid_t pid,
66 int* wstatus,
67 int options) {
68 return waitpid(pid, wstatus, options);
69}
70
71int MinijailedProcessRunner::RunSyncDestroy(
72 const std::vector<std::string>& argv,
73 brillo::Minijail* mj,
74 minijail* jail,
75 bool log_failures,
Jie Jiang0137f5d2020-12-16 16:23:59 +090076 std::string* output) {
Garrick Evans64a2df32018-12-12 16:53:46 +090077 std::vector<char*> args;
78 for (const auto& arg : argv) {
79 args.push_back(const_cast<char*>(arg.c_str()));
80 }
81 args.push_back(nullptr);
82
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090083 pid_t pid;
Jie Jiang0137f5d2020-12-16 16:23:59 +090084 int fd_stdout = -1;
85 int* stdout_p = output ? &fd_stdout : nullptr;
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090086 bool ran = mj->RunPipesAndDestroy(jail, args, &pid, nullptr /*stdin*/,
Jie Jiang0137f5d2020-12-16 16:23:59 +090087 stdout_p, nullptr /*stderr*/);
88 if (output) {
89 *output = ReadBlockingFDToStringAndClose(base::ScopedFD(fd_stdout));
90 }
91
92 int status = 0;
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090093 if (ran) {
94 ran = syscall_->WaitPID(pid, &status) == pid;
95 }
96
Garrick Evans64a2df32018-12-12 16:53:46 +090097 if (!ran) {
98 LOG(ERROR) << "Could not execute '" << base::JoinString(argv, " ") << "'";
99 } else if (log_failures && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
100 if (WIFEXITED(status)) {
Hugo Benichid4b19562019-06-21 13:00:12 +0900101 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
102 << "' exited with code " << WEXITSTATUS(status);
Garrick Evans64a2df32018-12-12 16:53:46 +0900103 } else if (WIFSIGNALED(status)) {
Hugo Benichid4b19562019-06-21 13:00:12 +0900104 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
105 << "' exited with signal " << WTERMSIG(status);
Garrick Evans64a2df32018-12-12 16:53:46 +0900106 } else {
Hugo Benichid4b19562019-06-21 13:00:12 +0900107 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
108 << "' exited with unknown status " << status;
Garrick Evans64a2df32018-12-12 16:53:46 +0900109 }
110 }
111 return ran && WIFEXITED(status) ? WEXITSTATUS(status) : -1;
112}
113
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900114int MinijailedProcessRunner::RunSync(const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900115 bool log_failures,
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900116 std::string* output) {
Jie Jiang0137f5d2020-12-16 16:23:59 +0900117 return RunSyncDestroy(argv, mj_, mj_->New(), log_failures, output);
Garrick Evans721eee52019-01-28 16:26:23 +0900118}
119
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900120void EnterChildProcessJail() {
121 brillo::Minijail* m = brillo::Minijail::GetInstance();
122 struct minijail* jail = m->New();
123
124 // Most of these return void, but DropRoot() can fail if the user/group
125 // does not exist.
126 CHECK(m->DropRoot(jail, kNetworkUnprivilegedUser, kNetworkUnprivilegedUser))
127 << "Could not drop root privileges";
Garrick Evans8e8e3472020-01-23 14:03:50 +0900128 m->UseCapabilities(jail, kNetRawCapMask);
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900129 m->Enter(jail);
130 m->Destroy(jail);
131}
132
Garrick Evans64a2df32018-12-12 16:53:46 +0900133MinijailedProcessRunner::MinijailedProcessRunner(brillo::Minijail* mj) {
134 mj_ = mj ? mj : brillo::Minijail::GetInstance();
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900135 syscall_ = std::make_unique<SyscallImpl>();
Garrick Evans64a2df32018-12-12 16:53:46 +0900136}
137
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900138MinijailedProcessRunner::MinijailedProcessRunner(
139 brillo::Minijail* mj, std::unique_ptr<SyscallImpl> syscall)
140 : mj_(mj), syscall_(std::move(syscall)) {}
141
Garrick Evans64a2df32018-12-12 16:53:46 +0900142int MinijailedProcessRunner::Run(const std::vector<std::string>& argv,
143 bool log_failures) {
144 minijail* jail = mj_->New();
145 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
Garrick Evans8e8e3472020-01-23 14:03:50 +0900146 mj_->UseCapabilities(jail, kNetRawAdminCapMask);
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900147 return RunSyncDestroy(argv, mj_, jail, log_failures, nullptr);
Garrick Evans64a2df32018-12-12 16:53:46 +0900148}
149
Garrick Evans8e8e3472020-01-23 14:03:50 +0900150int MinijailedProcessRunner::ip(const std::string& obj,
151 const std::string& cmd,
152 const std::vector<std::string>& argv,
153 bool log_failures) {
154 std::vector<std::string> args = {kIpPath, obj, cmd};
155 args.insert(args.end(), argv.begin(), argv.end());
156 return Run(args, log_failures);
157}
158
159int MinijailedProcessRunner::ip6(const std::string& obj,
160 const std::string& cmd,
161 const std::vector<std::string>& argv,
162 bool log_failures) {
163 std::vector<std::string> args = {kIpPath, "-6", obj, cmd};
164 args.insert(args.end(), argv.begin(), argv.end());
165 return Run(args, log_failures);
166}
167
168int MinijailedProcessRunner::iptables(const std::string& table,
169 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900170 bool log_failures,
171 std::string* output) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900172 std::vector<std::string> args = {kIptablesPath, "-t", table};
173 args.insert(args.end(), argv.begin(), argv.end());
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900174 return RunSync(args, log_failures, output);
Garrick Evans8e8e3472020-01-23 14:03:50 +0900175}
176
177int MinijailedProcessRunner::ip6tables(const std::string& table,
178 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900179 bool log_failures,
180 std::string* output) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900181 std::vector<std::string> args = {kIp6tablesPath, "-t", table};
182 args.insert(args.end(), argv.begin(), argv.end());
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900183 return RunSync(args, log_failures, output);
Garrick Evans8e8e3472020-01-23 14:03:50 +0900184}
185
186int MinijailedProcessRunner::modprobe_all(
187 const std::vector<std::string>& modules, bool log_failures) {
Garrick Evans78b414e2019-03-14 15:58:56 +0900188 minijail* jail = mj_->New();
189 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
190 mj_->UseCapabilities(jail, kModprobeCapMask);
191 std::vector<std::string> args = {kModprobePath, "-a"};
192 args.insert(args.end(), modules.begin(), modules.end());
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900193 return RunSyncDestroy(args, mj_, jail, log_failures, nullptr);
Garrick Evans78b414e2019-03-14 15:58:56 +0900194}
195
Garrick Evans8e8e3472020-01-23 14:03:50 +0900196int MinijailedProcessRunner::sysctl_w(const std::string& key,
197 const std::string& value,
198 bool log_failures) {
Garrick Evans6d227b92019-12-03 16:11:29 +0900199 std::vector<std::string> args = {kSysctlPath, "-w", key + "=" + value};
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900200 return RunSync(args, log_failures, nullptr);
Garrick Evans6d227b92019-12-03 16:11:29 +0900201}
202
Hugo Benichi33860d72020-07-09 16:34:01 +0900203int MinijailedProcessRunner::ip_netns_attach(const std::string& netns_name,
204 pid_t netns_pid,
205 bool log_failures) {
206 std::vector<std::string> args = {kIpPath, "netns", "attach", netns_name,
207 std::to_string(netns_pid)};
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900208 return RunSync(args, log_failures, nullptr);
Hugo Benichi33860d72020-07-09 16:34:01 +0900209}
210
211int MinijailedProcessRunner::ip_netns_delete(const std::string& netns_name,
212 bool log_failures) {
213 std::vector<std::string> args = {kIpPath, "netns", "delete", netns_name};
Jie Jiangc9a5cd52020-12-14 17:38:52 +0900214 return RunSync(args, log_failures, nullptr);
Hugo Benichi33860d72020-07-09 16:34:01 +0900215}
216
Garrick Evans3388a032020-03-24 11:25:55 +0900217} // namespace patchpanel