blob: 165e887fbfea1bc1ffe8f5a7a57680a5bcde79ea [file] [log] [blame]
Garrick Evans066dc2c2020-12-10 10:43:55 +09001// Copyright 2021 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
5#include "dns-proxy/controller.h"
6
Jason Jeremy Imanf6080422021-02-08 03:10:18 +09007#include <sys/capability.h>
8#include <sys/prctl.h>
9
Garrick Evans066dc2c2020-12-10 10:43:55 +090010#include <vector>
11
12#include <base/bind.h>
Qijiang Fan713061e2021-03-08 15:45:12 +090013#include <base/check.h>
Qijiang Fan886c4692021-02-19 11:54:10 +090014#include <base/notreached.h>
Garrick Evans066dc2c2020-12-10 10:43:55 +090015#include <base/process/launch.h>
16#include <base/threading/thread_task_runner_handle.h>
17#include <chromeos/scoped_minijail.h>
Garrick Evans48c84ef2021-01-28 11:29:42 +090018#include <shill/dbus-constants.h>
Garrick Evans066dc2c2020-12-10 10:43:55 +090019
20namespace dns_proxy {
21namespace {
22
23constexpr int kSubprocessRestartDelayMs = 900;
24constexpr char kSeccompPolicyPath[] =
25 "/usr/share/policy/dns-proxy-seccomp.policy";
26
27} // namespace
28
29Controller::Controller(const std::string& progname) : progname_(progname) {
30 default_proxy_deps_ = std::make_unique<DefaultProxyDeps>(base::Bind(
31 &Controller::EvalDefaultProxyDeps, weak_factory_.GetWeakPtr()));
32}
33
34Controller::~Controller() {
35 for (const auto& p : proxies_)
36 Kill(p);
Garrick Evans9c8797d2021-02-17 21:28:10 +090037
38 if (bus_)
39 bus_->ShutdownAndBlock();
Garrick Evans066dc2c2020-12-10 10:43:55 +090040}
41
42int Controller::OnInit() {
43 LOG(INFO) << "Starting DNS Proxy service";
44
Jason Jeremy Imanf6080422021-02-08 03:10:18 +090045 // Preserve CAP_NET_BIND_SERVICE so the child processes have the capability.
46 // Without the ambient set, file capabilities need to be used.
47 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0) !=
48 0) {
49 LOG(ERROR) << "Failed to add CAP_NET_BIND_SERVICE to the ambient set";
50 }
51
Garrick Evans066dc2c2020-12-10 10:43:55 +090052 // Handle subprocess lifecycle.
53 process_reaper_.Register(this);
54
55 /// Run after Daemon::OnInit()
56 base::ThreadTaskRunnerHandle::Get()->PostTask(
57 FROM_HERE, base::Bind(&Controller::Setup, weak_factory_.GetWeakPtr()));
58 return DBusDaemon::OnInit();
59}
60
61void Controller::OnShutdown(int*) {
62 LOG(INFO) << "Stopping DNS Proxy service";
63}
64
65void Controller::Setup() {
66 shill_.reset(new shill::Client(bus_));
67 shill_->Init();
Garrick Evans07c86d62021-02-18 09:31:42 +090068 shill_->RegisterDefaultDeviceChangedHandler(base::BindRepeating(
69 &Controller::OnDefaultDeviceChanged, weak_factory_.GetWeakPtr()));
Garrick Evans066dc2c2020-12-10 10:43:55 +090070
71 patchpanel_ = patchpanel::Client::New();
72 CHECK(patchpanel_) << "Failed to initialize patchpanel client";
73 patchpanel_->RegisterOnAvailableCallback(base::BindRepeating(
74 &Controller::OnPatchpanelReady, weak_factory_.GetWeakPtr()));
75
76 RunProxy(Proxy::Type::kSystem);
77}
78
79void Controller::OnPatchpanelReady(bool success) {
80 CHECK(success) << "Failed to connect to patchpanel";
81 patchpanel_->RegisterNetworkDeviceChangedSignalHandler(base::BindRepeating(
82 &Controller::OnVirtualDeviceChanged, weak_factory_.GetWeakPtr()));
83
84 // Process the current set of patchpanel devices and launch any required
85 // proxy processes.
86 for (const auto& d : patchpanel_->GetDevices())
87 VirtualDeviceAdded(d);
88}
89
90void Controller::RunProxy(Proxy::Type type, const std::string& ifname) {
91 ProxyProc proc(type, ifname);
92 auto it = proxies_.find(proc);
93 if (it != proxies_.end()) {
94 return;
95 }
96
97 ScopedMinijail jail(minijail_new());
98 minijail_namespace_net(jail.get());
99 minijail_no_new_privs(jail.get());
100 minijail_use_seccomp_filter(jail.get());
101 minijail_parse_seccomp_filters(jail.get(), kSeccompPolicyPath);
102 minijail_forward_signals(jail.get());
103 minijail_reset_signal_mask(jail.get());
104 minijail_reset_signal_handlers(jail.get());
105 minijail_run_as_init(jail.get());
106
107 std::vector<char*> argv;
108 const std::string flag_t = "--t=" + std::string(Proxy::TypeToString(type));
109 argv.push_back(const_cast<char*>(progname_.c_str()));
110 argv.push_back(const_cast<char*>(flag_t.c_str()));
Garrick Evans19541ba2021-02-18 13:24:03 +0900111 std::string flag_i = "--i=";
Garrick Evans066dc2c2020-12-10 10:43:55 +0900112 if (!ifname.empty()) {
Garrick Evans19541ba2021-02-18 13:24:03 +0900113 flag_i += ifname;
114 argv.push_back(const_cast<char*>(flag_i.c_str()));
Garrick Evans066dc2c2020-12-10 10:43:55 +0900115 }
116 argv.push_back(nullptr);
117
118 pid_t pid;
119 if (minijail_run_pid(jail.get(), argv[0], argv.data(), &pid) != 0) {
120 LOG(DFATAL) << "Failed to launch process for proxy " << proc;
121 return;
122 }
123 proc.pid = pid;
124 LOG(INFO) << "Launched process for proxy " << proc;
125
126 if (!process_reaper_.WatchForChild(
127 FROM_HERE, pid,
128 base::Bind(&Controller::OnProxyExit, weak_factory_.GetWeakPtr(),
129 pid))) {
130 LOG(ERROR) << "Failed to watch process for proxy " << proc
131 << " - did it crash after launch?";
132 return;
133 }
134
135 proxies_.emplace(proc);
136}
137
138void Controller::KillProxy(Proxy::Type type, const std::string& ifname) {
139 auto it = proxies_.find(ProxyProc(type, ifname));
140 if (it != proxies_.end()) {
141 Kill(*it);
142 proxies_.erase(it);
143 }
144}
145
146void Controller::Kill(const ProxyProc& proc) {
Garrick Evans48c84ef2021-01-28 11:29:42 +0900147 EvalProxyExit(proc);
Garrick Evans066dc2c2020-12-10 10:43:55 +0900148 process_reaper_.ForgetChild(proc.pid);
149 int rc = kill(proc.pid, SIGTERM);
150 if (rc < 0 && rc != ESRCH)
151 LOG(ERROR) << "Failed to kill process for proxy " << proc;
152}
153
154void Controller::OnProxyExit(pid_t pid, const siginfo_t& siginfo) {
155 process_reaper_.ForgetChild(pid);
156
157 // There will only ever be a handful of entries in this map so a linear scan
158 // will be trivial.
159 ProxyProc proc;
160 bool found = false;
161 for (auto it = proxies_.begin(); it != proxies_.end(); ++it) {
162 if (it->pid == pid) {
163 proc = *it;
164 proxies_.erase(it);
165 found = true;
166 break;
167 }
168 }
169 if (!found) {
170 LOG(ERROR) << "Unexpected process (" << pid << ") exit signal received";
171 return;
172 }
173
Garrick Evans48c84ef2021-01-28 11:29:42 +0900174 EvalProxyExit(proc);
175
Garrick Evans066dc2c2020-12-10 10:43:55 +0900176 switch (siginfo.si_code) {
177 case CLD_EXITED:
178 case CLD_DUMPED:
179 case CLD_KILLED:
180 case CLD_TRAPPED:
181 LOG(ERROR) << "Process for proxy [" << proc
182 << " was unexpectedly killed (" << siginfo.si_code << ":"
183 << siginfo.si_status << ") - attempting to restart";
184
185 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
186 FROM_HERE,
187 base::Bind(&Controller::RunProxy, weak_factory_.GetWeakPtr(),
188 proc.opts.type, proc.opts.ifname),
189 base::TimeDelta::FromMilliseconds(kSubprocessRestartDelayMs));
190 break;
191
192 case CLD_STOPPED:
193 LOG(WARNING) << "Process for proxy " << proc
194 << " was unexpectedly stopped";
195 break;
196
197 case CLD_CONTINUED:
198 LOG(WARNING) << "Process for proxy " << proc << " has continued";
199 break;
200
201 default:
202 NOTREACHED();
203 }
204}
205
Garrick Evans48c84ef2021-01-28 11:29:42 +0900206void Controller::EvalProxyExit(const ProxyProc& proc) {
207 if (proc.opts.type != Proxy::Type::kSystem)
208 return;
209
210 // Ensure the system proxy address is cleared from shill.
211 brillo::ErrorPtr error;
212 if (!shill_->ManagerProperties()->Set(shill::kDNSProxyIPv4AddressProperty, "",
213 &error))
214 LOG(WARNING) << "Failed to clear shill dns-proxy property for " << proc
215 << ": " << error->GetMessage();
216}
217
Garrick Evans066dc2c2020-12-10 10:43:55 +0900218void Controller::EvalDefaultProxyDeps(bool has_deps) {
219 has_deps ? RunProxy(Proxy::Type::kDefault) : KillProxy(Proxy::Type::kDefault);
220}
221
Garrick Evans07c86d62021-02-18 09:31:42 +0900222void Controller::OnDefaultDeviceChanged(
223 const shill::Client::Device* const device) {
224 // If the default service is lost, |device| will be null. In this case, we can
Garrick Evans066dc2c2020-12-10 10:43:55 +0900225 // still safely clear the VPN bit since when it reconnects, if the VPN is
226 // still up, the bit will be reset here.
Garrick Evans07c86d62021-02-18 09:31:42 +0900227 // Note that all VPN devices now use the kVPN type (so there is no need to
228 // check kPPP or kTunnel).
229 default_proxy_deps_->vpn_on(device && device->type ==
230 shill::Client::Device::Type::kVPN);
Garrick Evans066dc2c2020-12-10 10:43:55 +0900231}
232
233void Controller::OnVirtualDeviceChanged(
234 const patchpanel::NetworkDeviceChangedSignal& signal) {
235 switch (signal.event()) {
236 case patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED:
237 VirtualDeviceAdded(signal.device());
238 break;
239 case patchpanel::NetworkDeviceChangedSignal::DEVICE_REMOVED:
240 VirtualDeviceRemoved(signal.device());
241 break;
242 default:
243 NOTREACHED();
244 }
245}
246
247void Controller::VirtualDeviceAdded(const patchpanel::NetworkDevice& device) {
248 switch (device.guest_type()) {
249 case patchpanel::NetworkDevice::TERMINA_VM:
250 case patchpanel::NetworkDevice::PLUGIN_VM:
251 default_proxy_deps_->guest_up(device.ifname());
252 break;
Garrick Evans19541ba2021-02-18 13:24:03 +0900253 case patchpanel::NetworkDevice::ARC:
254 case patchpanel::NetworkDevice::ARCVM:
255 RunProxy(Proxy::Type::kARC, device.phys_ifname());
256 break;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900257 default:
258 break;
259 }
260}
261
262void Controller::VirtualDeviceRemoved(const patchpanel::NetworkDevice& device) {
263 switch (device.guest_type()) {
264 case patchpanel::NetworkDevice::TERMINA_VM:
265 case patchpanel::NetworkDevice::PLUGIN_VM:
266 default_proxy_deps_->guest_down(device.ifname());
267 break;
Garrick Evans19541ba2021-02-18 13:24:03 +0900268 case patchpanel::NetworkDevice::ARC:
269 case patchpanel::NetworkDevice::ARCVM:
270 KillProxy(Proxy::Type::kARC, device.phys_ifname());
271 break;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900272 default:
273 break;
274 }
275}
276
277} // namespace dns_proxy