blob: a433621dc03f3eed86e17c0576b92a8f433bb2e8 [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
11#include <base/logging.h>
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090012#include <base/posix/eintr_wrapper.h>
Garrick Evans2470caa2020-03-04 14:15:41 +090013#include <base/strings/string_number_conversions.h>
Garrick Evans64a2df32018-12-12 16:53:46 +090014#include <base/strings/string_util.h>
Simon Glass2b1da092020-05-21 12:24:16 -060015#include <brillo/process/process.h>
Garrick Evans64a2df32018-12-12 16:53:46 +090016
Garrick Evans3388a032020-03-24 11:25:55 +090017#include "patchpanel/net_util.h"
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090018
Garrick Evans3388a032020-03-24 11:25:55 +090019namespace patchpanel {
Garrick Evans64a2df32018-12-12 16:53:46 +090020
21namespace {
22
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090023constexpr char kUnprivilegedUser[] = "nobody";
Garrick Evans6776b502020-05-01 10:41:56 +090024constexpr char kNetworkUnprivilegedUser[] = "patchpaneld";
Garrick Evans6d227b92019-12-03 16:11:29 +090025constexpr char kChownCapMask = CAP_TO_MASK(CAP_CHOWN);
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.
32constexpr char kBrctlPath[] = "/sbin/brctl";
Garrick Evans6d227b92019-12-03 16:11:29 +090033constexpr char kChownPath[] = "/bin/chown";
Garrick Evans8e8e3472020-01-23 14:03:50 +090034constexpr char kIpPath[] = "/bin/ip";
35constexpr char kIptablesPath[] = "/sbin/iptables";
36constexpr char kIp6tablesPath[] = "/sbin/ip6tables";
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090037constexpr char kModprobePath[] = "/sbin/modprobe";
Garrick Evans6d227b92019-12-03 16:11:29 +090038constexpr char kSysctlPath[] = "/usr/sbin/sysctl";
Garrick Evans64a2df32018-12-12 16:53:46 +090039
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090040// An empty string will be returned if read fails.
41std::string ReadBlockingFDToString(int fd) {
42 static constexpr int kBufSize = 2048;
43 char buf[kBufSize] = {0};
44 std::string output;
45 while (true) {
46 ssize_t cnt = HANDLE_EINTR(read(fd, buf, kBufSize));
47 if (cnt == -1) {
48 PLOG(ERROR) << __func__ << " failed";
49 return {};
50 }
51
52 if (cnt == 0)
53 return output;
54
55 output.append({buf, static_cast<size_t>(cnt)});
56 }
57}
58
59} // namespace
60
61pid_t MinijailedProcessRunner::SyscallImpl::WaitPID(pid_t pid,
62 int* wstatus,
63 int options) {
64 return waitpid(pid, wstatus, options);
65}
66
67int MinijailedProcessRunner::RunSyncDestroy(
68 const std::vector<std::string>& argv,
69 brillo::Minijail* mj,
70 minijail* jail,
71 bool log_failures,
72 int* fd_stdout) {
Garrick Evans64a2df32018-12-12 16:53:46 +090073 std::vector<char*> args;
74 for (const auto& arg : argv) {
75 args.push_back(const_cast<char*>(arg.c_str()));
76 }
77 args.push_back(nullptr);
78
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090079 pid_t pid;
80 int status = 0;
81 bool ran = mj->RunPipesAndDestroy(jail, args, &pid, nullptr /*stdin*/,
82 fd_stdout, nullptr /*stderr*/);
83 if (ran) {
84 ran = syscall_->WaitPID(pid, &status) == pid;
85 }
86
Garrick Evans64a2df32018-12-12 16:53:46 +090087 if (!ran) {
88 LOG(ERROR) << "Could not execute '" << base::JoinString(argv, " ") << "'";
89 } else if (log_failures && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
90 if (WIFEXITED(status)) {
Hugo Benichid4b19562019-06-21 13:00:12 +090091 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
92 << "' exited with code " << WEXITSTATUS(status);
Garrick Evans64a2df32018-12-12 16:53:46 +090093 } else if (WIFSIGNALED(status)) {
Hugo Benichid4b19562019-06-21 13:00:12 +090094 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
95 << "' exited with signal " << WTERMSIG(status);
Garrick Evans64a2df32018-12-12 16:53:46 +090096 } else {
Hugo Benichid4b19562019-06-21 13:00:12 +090097 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
98 << "' exited with unknown status " << status;
Garrick Evans64a2df32018-12-12 16:53:46 +090099 }
100 }
101 return ran && WIFEXITED(status) ? WEXITSTATUS(status) : -1;
102}
103
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900104int MinijailedProcessRunner::RunSync(const std::vector<std::string>& argv,
105 brillo::Minijail* mj,
106 bool log_failures,
107 int* fd_stdout) {
108 return RunSyncDestroy(argv, mj, mj->New(), log_failures, fd_stdout);
Garrick Evans721eee52019-01-28 16:26:23 +0900109}
110
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900111void EnterChildProcessJail() {
112 brillo::Minijail* m = brillo::Minijail::GetInstance();
113 struct minijail* jail = m->New();
114
115 // Most of these return void, but DropRoot() can fail if the user/group
116 // does not exist.
117 CHECK(m->DropRoot(jail, kNetworkUnprivilegedUser, kNetworkUnprivilegedUser))
118 << "Could not drop root privileges";
Garrick Evans8e8e3472020-01-23 14:03:50 +0900119 m->UseCapabilities(jail, kNetRawCapMask);
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900120 m->Enter(jail);
121 m->Destroy(jail);
122}
123
Garrick Evans64a2df32018-12-12 16:53:46 +0900124MinijailedProcessRunner::MinijailedProcessRunner(brillo::Minijail* mj) {
125 mj_ = mj ? mj : brillo::Minijail::GetInstance();
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900126 syscall_ = std::make_unique<SyscallImpl>();
Garrick Evans64a2df32018-12-12 16:53:46 +0900127}
128
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900129MinijailedProcessRunner::MinijailedProcessRunner(
130 brillo::Minijail* mj, std::unique_ptr<SyscallImpl> syscall)
131 : mj_(mj), syscall_(std::move(syscall)) {}
132
Garrick Evans64a2df32018-12-12 16:53:46 +0900133int MinijailedProcessRunner::Run(const std::vector<std::string>& argv,
134 bool log_failures) {
135 minijail* jail = mj_->New();
136 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
Garrick Evans8e8e3472020-01-23 14:03:50 +0900137 mj_->UseCapabilities(jail, kNetRawAdminCapMask);
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900138 return RunSyncDestroy(argv, mj_, jail, log_failures, nullptr);
Garrick Evans64a2df32018-12-12 16:53:46 +0900139}
140
Garrick Evans8e8e3472020-01-23 14:03:50 +0900141int MinijailedProcessRunner::brctl(const std::string& cmd,
142 const std::vector<std::string>& argv,
143 bool log_failures) {
144 std::vector<std::string> args = {kBrctlPath, cmd};
145 args.insert(args.end(), argv.begin(), argv.end());
146 return Run(args, log_failures);
147}
148
149int MinijailedProcessRunner::chown(const std::string& uid,
150 const std::string& gid,
151 const std::string& file,
152 bool log_failures) {
153 minijail* jail = mj_->New();
154 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
155 mj_->UseCapabilities(jail, kChownCapMask);
156 std::vector<std::string> args = {kChownPath, uid + ":" + gid, file};
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900157 return RunSyncDestroy(args, mj_, jail, log_failures, nullptr);
Garrick Evans8e8e3472020-01-23 14:03:50 +0900158}
159
Garrick Evans8e8e3472020-01-23 14:03:50 +0900160int MinijailedProcessRunner::ip(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, obj, cmd};
165 args.insert(args.end(), argv.begin(), argv.end());
166 return Run(args, log_failures);
167}
168
169int MinijailedProcessRunner::ip6(const std::string& obj,
170 const std::string& cmd,
171 const std::vector<std::string>& argv,
172 bool log_failures) {
173 std::vector<std::string> args = {kIpPath, "-6", obj, cmd};
174 args.insert(args.end(), argv.begin(), argv.end());
175 return Run(args, log_failures);
176}
177
178int MinijailedProcessRunner::iptables(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 = {kIptablesPath, "-t", table};
183 args.insert(args.end(), argv.begin(), argv.end());
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900184 if (!output) {
185 return RunSync(args, mj_, log_failures, nullptr);
186 }
187
188 int fd_stdout;
189 int ret = RunSync(args, mj_, log_failures, &fd_stdout);
190 if (ret == 0) {
191 *output = ReadBlockingFDToString(fd_stdout);
192 }
193 return ret;
Garrick Evans8e8e3472020-01-23 14:03:50 +0900194}
195
196int MinijailedProcessRunner::ip6tables(const std::string& table,
197 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900198 bool log_failures,
199 std::string* output) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900200 std::vector<std::string> args = {kIp6tablesPath, "-t", table};
201 args.insert(args.end(), argv.begin(), argv.end());
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900202 if (!output) {
203 return RunSync(args, mj_, log_failures, nullptr);
204 }
205
206 int fd_stdout;
207 int ret = RunSync(args, mj_, log_failures, &fd_stdout);
208 if (ret == 0) {
209 *output = ReadBlockingFDToString(fd_stdout);
210 }
211 return ret;
Garrick Evans8e8e3472020-01-23 14:03:50 +0900212}
213
214int MinijailedProcessRunner::modprobe_all(
215 const std::vector<std::string>& modules, bool log_failures) {
Garrick Evans78b414e2019-03-14 15:58:56 +0900216 minijail* jail = mj_->New();
217 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
218 mj_->UseCapabilities(jail, kModprobeCapMask);
219 std::vector<std::string> args = {kModprobePath, "-a"};
220 args.insert(args.end(), modules.begin(), modules.end());
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900221 return RunSyncDestroy(args, mj_, jail, log_failures, nullptr);
Garrick Evans78b414e2019-03-14 15:58:56 +0900222}
223
Garrick Evans8e8e3472020-01-23 14:03:50 +0900224int MinijailedProcessRunner::sysctl_w(const std::string& key,
225 const std::string& value,
226 bool log_failures) {
Garrick Evans6d227b92019-12-03 16:11:29 +0900227 std::vector<std::string> args = {kSysctlPath, "-w", key + "=" + value};
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900228 return RunSync(args, mj_, log_failures, nullptr);
Garrick Evans6d227b92019-12-03 16:11:29 +0900229}
230
Hugo Benichi33860d72020-07-09 16:34:01 +0900231int MinijailedProcessRunner::ip_netns_attach(const std::string& netns_name,
232 pid_t netns_pid,
233 bool log_failures) {
234 std::vector<std::string> args = {kIpPath, "netns", "attach", netns_name,
235 std::to_string(netns_pid)};
236 return RunSync(args, mj_, log_failures, nullptr);
237}
238
239int MinijailedProcessRunner::ip_netns_delete(const std::string& netns_name,
240 bool log_failures) {
241 std::vector<std::string> args = {kIpPath, "netns", "delete", netns_name};
242 return RunSync(args, mj_, log_failures, nullptr);
243}
244
Garrick Evans3388a032020-03-24 11:25:55 +0900245} // namespace patchpanel