blob: b05049869065476aad9144ce8376f31ac1b45b0d [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";
38constexpr char kNsEnterPath[] = "/usr/bin/nsenter";
Garrick Evans6d227b92019-12-03 16:11:29 +090039constexpr char kSysctlPath[] = "/usr/sbin/sysctl";
Garrick Evans64a2df32018-12-12 16:53:46 +090040
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090041// An empty string will be returned if read fails.
42std::string ReadBlockingFDToString(int fd) {
43 static constexpr int kBufSize = 2048;
44 char buf[kBufSize] = {0};
45 std::string output;
46 while (true) {
47 ssize_t cnt = HANDLE_EINTR(read(fd, buf, kBufSize));
48 if (cnt == -1) {
49 PLOG(ERROR) << __func__ << " failed";
50 return {};
51 }
52
53 if (cnt == 0)
54 return output;
55
56 output.append({buf, static_cast<size_t>(cnt)});
57 }
58}
59
60} // namespace
61
62pid_t MinijailedProcessRunner::SyscallImpl::WaitPID(pid_t pid,
63 int* wstatus,
64 int options) {
65 return waitpid(pid, wstatus, options);
66}
67
68int MinijailedProcessRunner::RunSyncDestroy(
69 const std::vector<std::string>& argv,
70 brillo::Minijail* mj,
71 minijail* jail,
72 bool log_failures,
73 int* fd_stdout) {
Garrick Evans64a2df32018-12-12 16:53:46 +090074 std::vector<char*> args;
75 for (const auto& arg : argv) {
76 args.push_back(const_cast<char*>(arg.c_str()));
77 }
78 args.push_back(nullptr);
79
Jie Jiangcf5ce9c2020-07-14 17:22:03 +090080 pid_t pid;
81 int status = 0;
82 bool ran = mj->RunPipesAndDestroy(jail, args, &pid, nullptr /*stdin*/,
83 fd_stdout, nullptr /*stderr*/);
84 if (ran) {
85 ran = syscall_->WaitPID(pid, &status) == pid;
86 }
87
Garrick Evans64a2df32018-12-12 16:53:46 +090088 if (!ran) {
89 LOG(ERROR) << "Could not execute '" << base::JoinString(argv, " ") << "'";
90 } else if (log_failures && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
91 if (WIFEXITED(status)) {
Hugo Benichid4b19562019-06-21 13:00:12 +090092 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
93 << "' exited with code " << WEXITSTATUS(status);
Garrick Evans64a2df32018-12-12 16:53:46 +090094 } else if (WIFSIGNALED(status)) {
Hugo Benichid4b19562019-06-21 13:00:12 +090095 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
96 << "' exited with signal " << WTERMSIG(status);
Garrick Evans64a2df32018-12-12 16:53:46 +090097 } else {
Hugo Benichid4b19562019-06-21 13:00:12 +090098 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
99 << "' exited with unknown status " << status;
Garrick Evans64a2df32018-12-12 16:53:46 +0900100 }
101 }
102 return ran && WIFEXITED(status) ? WEXITSTATUS(status) : -1;
103}
104
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900105int MinijailedProcessRunner::RunSync(const std::vector<std::string>& argv,
106 brillo::Minijail* mj,
107 bool log_failures,
108 int* fd_stdout) {
109 return RunSyncDestroy(argv, mj, mj->New(), log_failures, fd_stdout);
Garrick Evans721eee52019-01-28 16:26:23 +0900110}
111
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900112void EnterChildProcessJail() {
113 brillo::Minijail* m = brillo::Minijail::GetInstance();
114 struct minijail* jail = m->New();
115
116 // Most of these return void, but DropRoot() can fail if the user/group
117 // does not exist.
118 CHECK(m->DropRoot(jail, kNetworkUnprivilegedUser, kNetworkUnprivilegedUser))
119 << "Could not drop root privileges";
Garrick Evans8e8e3472020-01-23 14:03:50 +0900120 m->UseCapabilities(jail, kNetRawCapMask);
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900121 m->Enter(jail);
122 m->Destroy(jail);
123}
124
Garrick Evans64a2df32018-12-12 16:53:46 +0900125MinijailedProcessRunner::MinijailedProcessRunner(brillo::Minijail* mj) {
126 mj_ = mj ? mj : brillo::Minijail::GetInstance();
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900127 syscall_ = std::make_unique<SyscallImpl>();
Garrick Evans64a2df32018-12-12 16:53:46 +0900128}
129
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900130MinijailedProcessRunner::MinijailedProcessRunner(
131 brillo::Minijail* mj, std::unique_ptr<SyscallImpl> syscall)
132 : mj_(mj), syscall_(std::move(syscall)) {}
133
Garrick Evans64a2df32018-12-12 16:53:46 +0900134int MinijailedProcessRunner::Run(const std::vector<std::string>& argv,
135 bool log_failures) {
136 minijail* jail = mj_->New();
137 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
Garrick Evans8e8e3472020-01-23 14:03:50 +0900138 mj_->UseCapabilities(jail, kNetRawAdminCapMask);
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900139 return RunSyncDestroy(argv, mj_, jail, log_failures, nullptr);
Garrick Evans64a2df32018-12-12 16:53:46 +0900140}
141
Garrick Evans2470caa2020-03-04 14:15:41 +0900142int MinijailedProcessRunner::RestoreDefaultNamespace(const std::string& ifname,
143 pid_t pid) {
Qijiang Fan26fd9222020-03-31 20:16:40 +0900144 return RunSync({kNsEnterPath, "-t", base::NumberToString(pid), "-n", "--",
Garrick Evans2470caa2020-03-04 14:15:41 +0900145 kIpPath, "link", "set", ifname, "netns", "1"},
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900146 mj_, true, nullptr);
Garrick Evans64a2df32018-12-12 16:53:46 +0900147}
148
Garrick Evans8e8e3472020-01-23 14:03:50 +0900149int MinijailedProcessRunner::brctl(const std::string& cmd,
150 const std::vector<std::string>& argv,
151 bool log_failures) {
152 std::vector<std::string> args = {kBrctlPath, cmd};
153 args.insert(args.end(), argv.begin(), argv.end());
154 return Run(args, log_failures);
155}
156
157int MinijailedProcessRunner::chown(const std::string& uid,
158 const std::string& gid,
159 const std::string& file,
160 bool log_failures) {
161 minijail* jail = mj_->New();
162 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
163 mj_->UseCapabilities(jail, kChownCapMask);
164 std::vector<std::string> args = {kChownPath, uid + ":" + gid, file};
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900165 return RunSyncDestroy(args, mj_, jail, log_failures, nullptr);
Garrick Evans8e8e3472020-01-23 14:03:50 +0900166}
167
Garrick Evans8e8e3472020-01-23 14:03:50 +0900168int MinijailedProcessRunner::ip(const std::string& obj,
169 const std::string& cmd,
170 const std::vector<std::string>& argv,
171 bool log_failures) {
172 std::vector<std::string> args = {kIpPath, obj, cmd};
173 args.insert(args.end(), argv.begin(), argv.end());
174 return Run(args, log_failures);
175}
176
177int MinijailedProcessRunner::ip6(const std::string& obj,
178 const std::string& cmd,
179 const std::vector<std::string>& argv,
180 bool log_failures) {
181 std::vector<std::string> args = {kIpPath, "-6", obj, cmd};
182 args.insert(args.end(), argv.begin(), argv.end());
183 return Run(args, log_failures);
184}
185
186int MinijailedProcessRunner::iptables(const std::string& table,
187 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900188 bool log_failures,
189 std::string* output) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900190 std::vector<std::string> args = {kIptablesPath, "-t", table};
191 args.insert(args.end(), argv.begin(), argv.end());
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900192 if (!output) {
193 return RunSync(args, mj_, log_failures, nullptr);
194 }
195
196 int fd_stdout;
197 int ret = RunSync(args, mj_, log_failures, &fd_stdout);
198 if (ret == 0) {
199 *output = ReadBlockingFDToString(fd_stdout);
200 }
201 return ret;
Garrick Evans8e8e3472020-01-23 14:03:50 +0900202}
203
204int MinijailedProcessRunner::ip6tables(const std::string& table,
205 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900206 bool log_failures,
207 std::string* output) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900208 std::vector<std::string> args = {kIp6tablesPath, "-t", table};
209 args.insert(args.end(), argv.begin(), argv.end());
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900210 if (!output) {
211 return RunSync(args, mj_, log_failures, nullptr);
212 }
213
214 int fd_stdout;
215 int ret = RunSync(args, mj_, log_failures, &fd_stdout);
216 if (ret == 0) {
217 *output = ReadBlockingFDToString(fd_stdout);
218 }
219 return ret;
Garrick Evans8e8e3472020-01-23 14:03:50 +0900220}
221
222int MinijailedProcessRunner::modprobe_all(
223 const std::vector<std::string>& modules, bool log_failures) {
Garrick Evans78b414e2019-03-14 15:58:56 +0900224 minijail* jail = mj_->New();
225 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
226 mj_->UseCapabilities(jail, kModprobeCapMask);
227 std::vector<std::string> args = {kModprobePath, "-a"};
228 args.insert(args.end(), modules.begin(), modules.end());
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900229 return RunSyncDestroy(args, mj_, jail, log_failures, nullptr);
Garrick Evans78b414e2019-03-14 15:58:56 +0900230}
231
Garrick Evans8e8e3472020-01-23 14:03:50 +0900232int MinijailedProcessRunner::sysctl_w(const std::string& key,
233 const std::string& value,
234 bool log_failures) {
Garrick Evans6d227b92019-12-03 16:11:29 +0900235 std::vector<std::string> args = {kSysctlPath, "-w", key + "=" + value};
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900236 return RunSync(args, mj_, log_failures, nullptr);
Garrick Evans6d227b92019-12-03 16:11:29 +0900237}
238
Garrick Evans3388a032020-03-24 11:25:55 +0900239} // namespace patchpanel