blob: d779b39914a525ceeb035509e5f5ae2f5b2fd486 [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>
8
9#include <base/logging.h>
Garrick Evans2470caa2020-03-04 14:15:41 +090010#include <base/strings/string_number_conversions.h>
Garrick Evans64a2df32018-12-12 16:53:46 +090011#include <base/strings/string_util.h>
12#include <brillo/process.h>
13
Garrick Evans3388a032020-03-24 11:25:55 +090014#include "patchpanel/net_util.h"
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090015
Garrick Evans3388a032020-03-24 11:25:55 +090016namespace patchpanel {
Garrick Evans64a2df32018-12-12 16:53:46 +090017
18namespace {
19
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090020constexpr char kUnprivilegedUser[] = "nobody";
21constexpr char kNetworkUnprivilegedUser[] = "arc-networkd";
Garrick Evans6d227b92019-12-03 16:11:29 +090022constexpr char kChownCapMask = CAP_TO_MASK(CAP_CHOWN);
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090023constexpr uint64_t kModprobeCapMask = CAP_TO_MASK(CAP_SYS_MODULE);
Garrick Evans8e8e3472020-01-23 14:03:50 +090024constexpr uint64_t kNetRawCapMask = CAP_TO_MASK(CAP_NET_RAW);
25constexpr uint64_t kNetRawAdminCapMask =
26 CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_RAW);
27
28// These match what is used in iptables.cc in firewalld.
29constexpr char kBrctlPath[] = "/sbin/brctl";
Garrick Evans6d227b92019-12-03 16:11:29 +090030constexpr char kChownPath[] = "/bin/chown";
Garrick Evans8e8e3472020-01-23 14:03:50 +090031constexpr char kIpPath[] = "/bin/ip";
32constexpr char kIptablesPath[] = "/sbin/iptables";
33constexpr char kIp6tablesPath[] = "/sbin/ip6tables";
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090034constexpr char kModprobePath[] = "/sbin/modprobe";
35constexpr char kNsEnterPath[] = "/usr/bin/nsenter";
Garrick Evans6d227b92019-12-03 16:11:29 +090036constexpr char kSysctlPath[] = "/usr/sbin/sysctl";
Garrick Evans3bd06372020-03-23 10:42:58 +090037constexpr char kSentinelFile[] = "/dev/.arc_network_ready";
38constexpr char kTouchPath[] = "/system/bin/touch";
Garrick Evans64a2df32018-12-12 16:53:46 +090039
40int RunSyncDestroy(const std::vector<std::string>& argv,
41 brillo::Minijail* mj,
42 minijail* jail,
43 bool log_failures) {
44 std::vector<char*> args;
45 for (const auto& arg : argv) {
46 args.push_back(const_cast<char*>(arg.c_str()));
47 }
48 args.push_back(nullptr);
49
50 int status;
51 bool ran = mj->RunSyncAndDestroy(jail, args, &status);
52 if (!ran) {
53 LOG(ERROR) << "Could not execute '" << base::JoinString(argv, " ") << "'";
54 } else if (log_failures && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
55 if (WIFEXITED(status)) {
Hugo Benichid4b19562019-06-21 13:00:12 +090056 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
57 << "' exited with code " << WEXITSTATUS(status);
Garrick Evans64a2df32018-12-12 16:53:46 +090058 } else if (WIFSIGNALED(status)) {
Hugo Benichid4b19562019-06-21 13:00:12 +090059 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
60 << "' exited with signal " << WTERMSIG(status);
Garrick Evans64a2df32018-12-12 16:53:46 +090061 } else {
Hugo Benichid4b19562019-06-21 13:00:12 +090062 LOG(WARNING) << "Subprocess '" << base::JoinString(argv, " ")
63 << "' exited with unknown status " << status;
Garrick Evans64a2df32018-12-12 16:53:46 +090064 }
65 }
66 return ran && WIFEXITED(status) ? WEXITSTATUS(status) : -1;
67}
68
Garrick Evans721eee52019-01-28 16:26:23 +090069int RunSync(const std::vector<std::string>& argv,
70 brillo::Minijail* mj,
71 bool log_failures) {
72 return RunSyncDestroy(argv, mj, mj->New(), log_failures);
73}
74
Garrick Evans64a2df32018-12-12 16:53:46 +090075} // namespace
76
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090077void EnterChildProcessJail() {
78 brillo::Minijail* m = brillo::Minijail::GetInstance();
79 struct minijail* jail = m->New();
80
81 // Most of these return void, but DropRoot() can fail if the user/group
82 // does not exist.
83 CHECK(m->DropRoot(jail, kNetworkUnprivilegedUser, kNetworkUnprivilegedUser))
84 << "Could not drop root privileges";
Garrick Evans8e8e3472020-01-23 14:03:50 +090085 m->UseCapabilities(jail, kNetRawCapMask);
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +090086 m->Enter(jail);
87 m->Destroy(jail);
88}
89
Garrick Evans64a2df32018-12-12 16:53:46 +090090MinijailedProcessRunner::MinijailedProcessRunner(brillo::Minijail* mj) {
91 mj_ = mj ? mj : brillo::Minijail::GetInstance();
92}
93
94int MinijailedProcessRunner::Run(const std::vector<std::string>& argv,
95 bool log_failures) {
96 minijail* jail = mj_->New();
97 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
Garrick Evans8e8e3472020-01-23 14:03:50 +090098 mj_->UseCapabilities(jail, kNetRawAdminCapMask);
Garrick Evans64a2df32018-12-12 16:53:46 +090099 return RunSyncDestroy(argv, mj_, jail, log_failures);
100}
101
Garrick Evans2470caa2020-03-04 14:15:41 +0900102int MinijailedProcessRunner::RestoreDefaultNamespace(const std::string& ifname,
103 pid_t pid) {
Qijiang Fan26fd9222020-03-31 20:16:40 +0900104 return RunSync({kNsEnterPath, "-t", base::NumberToString(pid), "-n", "--",
Garrick Evans2470caa2020-03-04 14:15:41 +0900105 kIpPath, "link", "set", ifname, "netns", "1"},
Garrick Evans7a1a9ee2020-01-28 11:03:57 +0900106 mj_, true);
Garrick Evans64a2df32018-12-12 16:53:46 +0900107}
108
Garrick Evans3bd06372020-03-23 10:42:58 +0900109int MinijailedProcessRunner::WriteSentinelToContainer(pid_t pid) {
Qijiang Fan26fd9222020-03-31 20:16:40 +0900110 return RunSync({kNsEnterPath, "-t", base::NumberToString(pid), "--mount",
Garrick Evans3bd06372020-03-23 10:42:58 +0900111 "--pid", "--", kTouchPath, kSentinelFile},
112 mj_, true);
113}
114
Garrick Evans8e8e3472020-01-23 14:03:50 +0900115int MinijailedProcessRunner::brctl(const std::string& cmd,
116 const std::vector<std::string>& argv,
117 bool log_failures) {
118 std::vector<std::string> args = {kBrctlPath, cmd};
119 args.insert(args.end(), argv.begin(), argv.end());
120 return Run(args, log_failures);
121}
122
123int MinijailedProcessRunner::chown(const std::string& uid,
124 const std::string& gid,
125 const std::string& file,
126 bool log_failures) {
127 minijail* jail = mj_->New();
128 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
129 mj_->UseCapabilities(jail, kChownCapMask);
130 std::vector<std::string> args = {kChownPath, uid + ":" + gid, file};
131 return RunSyncDestroy(args, mj_, jail, log_failures);
132}
133
Garrick Evans8e8e3472020-01-23 14:03:50 +0900134int MinijailedProcessRunner::ip(const std::string& obj,
135 const std::string& cmd,
136 const std::vector<std::string>& argv,
137 bool log_failures) {
138 std::vector<std::string> args = {kIpPath, obj, cmd};
139 args.insert(args.end(), argv.begin(), argv.end());
140 return Run(args, log_failures);
141}
142
143int MinijailedProcessRunner::ip6(const std::string& obj,
144 const std::string& cmd,
145 const std::vector<std::string>& argv,
146 bool log_failures) {
147 std::vector<std::string> args = {kIpPath, "-6", obj, cmd};
148 args.insert(args.end(), argv.begin(), argv.end());
149 return Run(args, log_failures);
150}
151
152int MinijailedProcessRunner::iptables(const std::string& table,
153 const std::vector<std::string>& argv,
154 bool log_failures) {
155 std::vector<std::string> args = {kIptablesPath, "-t", table};
156 args.insert(args.end(), argv.begin(), argv.end());
157 return RunSync(args, mj_, log_failures);
158}
159
160int MinijailedProcessRunner::ip6tables(const std::string& table,
161 const std::vector<std::string>& argv,
162 bool log_failures) {
163 std::vector<std::string> args = {kIp6tablesPath, "-t", table};
164 args.insert(args.end(), argv.begin(), argv.end());
165 return RunSync(args, mj_, log_failures);
166}
167
168int MinijailedProcessRunner::modprobe_all(
169 const std::vector<std::string>& modules, bool log_failures) {
Garrick Evans78b414e2019-03-14 15:58:56 +0900170 minijail* jail = mj_->New();
171 CHECK(mj_->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
172 mj_->UseCapabilities(jail, kModprobeCapMask);
173 std::vector<std::string> args = {kModprobePath, "-a"};
174 args.insert(args.end(), modules.begin(), modules.end());
Garrick Evans8e8e3472020-01-23 14:03:50 +0900175 return RunSyncDestroy(args, mj_, jail, log_failures);
Garrick Evans78b414e2019-03-14 15:58:56 +0900176}
177
Garrick Evans8e8e3472020-01-23 14:03:50 +0900178int MinijailedProcessRunner::sysctl_w(const std::string& key,
179 const std::string& value,
180 bool log_failures) {
Garrick Evans6d227b92019-12-03 16:11:29 +0900181 std::vector<std::string> args = {kSysctlPath, "-w", key + "=" + value};
Garrick Evans8e8e3472020-01-23 14:03:50 +0900182 return RunSync(args, mj_, log_failures);
Garrick Evans6d227b92019-12-03 16:11:29 +0900183}
184
Garrick Evans3388a032020-03-24 11:25:55 +0900185} // namespace patchpanel