blob: 289ada6b4d89d82f4de765fa1fce3ee6ced123ca [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>
13#include "base/notreached.h"
14#include <base/process/launch.h>
15#include <base/threading/thread_task_runner_handle.h>
16#include <chromeos/scoped_minijail.h>
Garrick Evans48c84ef2021-01-28 11:29:42 +090017#include <shill/dbus-constants.h>
Garrick Evans066dc2c2020-12-10 10:43:55 +090018
19namespace dns_proxy {
20namespace {
21
22constexpr int kSubprocessRestartDelayMs = 900;
23constexpr char kSeccompPolicyPath[] =
24 "/usr/share/policy/dns-proxy-seccomp.policy";
25
26} // namespace
27
28Controller::Controller(const std::string& progname) : progname_(progname) {
29 default_proxy_deps_ = std::make_unique<DefaultProxyDeps>(base::Bind(
30 &Controller::EvalDefaultProxyDeps, weak_factory_.GetWeakPtr()));
31}
32
33Controller::~Controller() {
34 for (const auto& p : proxies_)
35 Kill(p);
36}
37
38int Controller::OnInit() {
39 LOG(INFO) << "Starting DNS Proxy service";
40
Jason Jeremy Imanf6080422021-02-08 03:10:18 +090041 // Preserve CAP_NET_BIND_SERVICE so the child processes have the capability.
42 // Without the ambient set, file capabilities need to be used.
43 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0) !=
44 0) {
45 LOG(ERROR) << "Failed to add CAP_NET_BIND_SERVICE to the ambient set";
46 }
47
Garrick Evans066dc2c2020-12-10 10:43:55 +090048 // Handle subprocess lifecycle.
49 process_reaper_.Register(this);
50
51 /// Run after Daemon::OnInit()
52 base::ThreadTaskRunnerHandle::Get()->PostTask(
53 FROM_HERE, base::Bind(&Controller::Setup, weak_factory_.GetWeakPtr()));
54 return DBusDaemon::OnInit();
55}
56
57void Controller::OnShutdown(int*) {
58 LOG(INFO) << "Stopping DNS Proxy service";
59}
60
61void Controller::Setup() {
62 shill_.reset(new shill::Client(bus_));
63 shill_->Init();
64 shill_->RegisterDefaultServiceChangedHandler(base::BindRepeating(
65 &Controller::OnDefaultServiceChanged, weak_factory_.GetWeakPtr()));
66
67 patchpanel_ = patchpanel::Client::New();
68 CHECK(patchpanel_) << "Failed to initialize patchpanel client";
69 patchpanel_->RegisterOnAvailableCallback(base::BindRepeating(
70 &Controller::OnPatchpanelReady, weak_factory_.GetWeakPtr()));
71
72 RunProxy(Proxy::Type::kSystem);
73}
74
75void Controller::OnPatchpanelReady(bool success) {
76 CHECK(success) << "Failed to connect to patchpanel";
77 patchpanel_->RegisterNetworkDeviceChangedSignalHandler(base::BindRepeating(
78 &Controller::OnVirtualDeviceChanged, weak_factory_.GetWeakPtr()));
79
80 // Process the current set of patchpanel devices and launch any required
81 // proxy processes.
82 for (const auto& d : patchpanel_->GetDevices())
83 VirtualDeviceAdded(d);
84}
85
86void Controller::RunProxy(Proxy::Type type, const std::string& ifname) {
87 ProxyProc proc(type, ifname);
88 auto it = proxies_.find(proc);
89 if (it != proxies_.end()) {
90 return;
91 }
92
93 ScopedMinijail jail(minijail_new());
94 minijail_namespace_net(jail.get());
95 minijail_no_new_privs(jail.get());
96 minijail_use_seccomp_filter(jail.get());
97 minijail_parse_seccomp_filters(jail.get(), kSeccompPolicyPath);
98 minijail_forward_signals(jail.get());
99 minijail_reset_signal_mask(jail.get());
100 minijail_reset_signal_handlers(jail.get());
101 minijail_run_as_init(jail.get());
102
103 std::vector<char*> argv;
104 const std::string flag_t = "--t=" + std::string(Proxy::TypeToString(type));
105 argv.push_back(const_cast<char*>(progname_.c_str()));
106 argv.push_back(const_cast<char*>(flag_t.c_str()));
107 if (!ifname.empty()) {
108 const std::string flag_if = "--i=" + ifname;
109 argv.push_back(const_cast<char*>(flag_if.c_str()));
110 }
111 argv.push_back(nullptr);
112
113 pid_t pid;
114 if (minijail_run_pid(jail.get(), argv[0], argv.data(), &pid) != 0) {
115 LOG(DFATAL) << "Failed to launch process for proxy " << proc;
116 return;
117 }
118 proc.pid = pid;
119 LOG(INFO) << "Launched process for proxy " << proc;
120
121 if (!process_reaper_.WatchForChild(
122 FROM_HERE, pid,
123 base::Bind(&Controller::OnProxyExit, weak_factory_.GetWeakPtr(),
124 pid))) {
125 LOG(ERROR) << "Failed to watch process for proxy " << proc
126 << " - did it crash after launch?";
127 return;
128 }
129
130 proxies_.emplace(proc);
131}
132
133void Controller::KillProxy(Proxy::Type type, const std::string& ifname) {
134 auto it = proxies_.find(ProxyProc(type, ifname));
135 if (it != proxies_.end()) {
136 Kill(*it);
137 proxies_.erase(it);
138 }
139}
140
141void Controller::Kill(const ProxyProc& proc) {
Garrick Evans48c84ef2021-01-28 11:29:42 +0900142 EvalProxyExit(proc);
Garrick Evans066dc2c2020-12-10 10:43:55 +0900143 process_reaper_.ForgetChild(proc.pid);
144 int rc = kill(proc.pid, SIGTERM);
145 if (rc < 0 && rc != ESRCH)
146 LOG(ERROR) << "Failed to kill process for proxy " << proc;
147}
148
149void Controller::OnProxyExit(pid_t pid, const siginfo_t& siginfo) {
150 process_reaper_.ForgetChild(pid);
151
152 // There will only ever be a handful of entries in this map so a linear scan
153 // will be trivial.
154 ProxyProc proc;
155 bool found = false;
156 for (auto it = proxies_.begin(); it != proxies_.end(); ++it) {
157 if (it->pid == pid) {
158 proc = *it;
159 proxies_.erase(it);
160 found = true;
161 break;
162 }
163 }
164 if (!found) {
165 LOG(ERROR) << "Unexpected process (" << pid << ") exit signal received";
166 return;
167 }
168
Garrick Evans48c84ef2021-01-28 11:29:42 +0900169 EvalProxyExit(proc);
170
Garrick Evans066dc2c2020-12-10 10:43:55 +0900171 switch (siginfo.si_code) {
172 case CLD_EXITED:
173 case CLD_DUMPED:
174 case CLD_KILLED:
175 case CLD_TRAPPED:
176 LOG(ERROR) << "Process for proxy [" << proc
177 << " was unexpectedly killed (" << siginfo.si_code << ":"
178 << siginfo.si_status << ") - attempting to restart";
179
180 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
181 FROM_HERE,
182 base::Bind(&Controller::RunProxy, weak_factory_.GetWeakPtr(),
183 proc.opts.type, proc.opts.ifname),
184 base::TimeDelta::FromMilliseconds(kSubprocessRestartDelayMs));
185 break;
186
187 case CLD_STOPPED:
188 LOG(WARNING) << "Process for proxy " << proc
189 << " was unexpectedly stopped";
190 break;
191
192 case CLD_CONTINUED:
193 LOG(WARNING) << "Process for proxy " << proc << " has continued";
194 break;
195
196 default:
197 NOTREACHED();
198 }
199}
200
Garrick Evans48c84ef2021-01-28 11:29:42 +0900201void Controller::EvalProxyExit(const ProxyProc& proc) {
202 if (proc.opts.type != Proxy::Type::kSystem)
203 return;
204
205 // Ensure the system proxy address is cleared from shill.
206 brillo::ErrorPtr error;
207 if (!shill_->ManagerProperties()->Set(shill::kDNSProxyIPv4AddressProperty, "",
208 &error))
209 LOG(WARNING) << "Failed to clear shill dns-proxy property for " << proc
210 << ": " << error->GetMessage();
211}
212
Garrick Evans066dc2c2020-12-10 10:43:55 +0900213void Controller::EvalDefaultProxyDeps(bool has_deps) {
214 has_deps ? RunProxy(Proxy::Type::kDefault) : KillProxy(Proxy::Type::kDefault);
215}
216
217void Controller::OnDefaultServiceChanged(const std::string& type) {
218 // If the default service is lost, |type| will be empty. In this case, we can
219 // still safely clear the VPN bit since when it reconnects, if the VPN is
220 // still up, the bit will be reset here.
221 default_proxy_deps_->vpn_on(type == shill::kTypeVPN);
222}
223
224void Controller::OnVirtualDeviceChanged(
225 const patchpanel::NetworkDeviceChangedSignal& signal) {
226 switch (signal.event()) {
227 case patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED:
228 VirtualDeviceAdded(signal.device());
229 break;
230 case patchpanel::NetworkDeviceChangedSignal::DEVICE_REMOVED:
231 VirtualDeviceRemoved(signal.device());
232 break;
233 default:
234 NOTREACHED();
235 }
236}
237
238void Controller::VirtualDeviceAdded(const patchpanel::NetworkDevice& device) {
239 switch (device.guest_type()) {
240 case patchpanel::NetworkDevice::TERMINA_VM:
241 case patchpanel::NetworkDevice::PLUGIN_VM:
242 default_proxy_deps_->guest_up(device.ifname());
243 break;
244 default:
245 break;
246 }
247}
248
249void Controller::VirtualDeviceRemoved(const patchpanel::NetworkDevice& device) {
250 switch (device.guest_type()) {
251 case patchpanel::NetworkDevice::TERMINA_VM:
252 case patchpanel::NetworkDevice::PLUGIN_VM:
253 default_proxy_deps_->guest_down(device.ifname());
254 break;
255 default:
256 break;
257 }
258}
259
260} // namespace dns_proxy