blob: b305980f542c1bffd87fbc8085d8505d30849ae8 [file] [log] [blame]
Andreea Costinas942284d2020-01-28 16:28:40 +01001// Copyright 2020 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#include "system-proxy/system_proxy_adaptor.h"
5
6#include <string>
7#include <utility>
8#include <vector>
9
10#include <base/location.h>
11#include <brillo/dbus/dbus_object.h>
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010012#include <brillo/message_loops/message_loop.h>
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020013#include <chromeos/dbus/service_constants.h>
14#include <chromeos/patchpanel/client.h>
15#include <dbus/object_proxy.h>
Andreea Costinas942284d2020-01-28 16:28:40 +010016
17#include "system_proxy/proto_bindings/system_proxy_service.pb.h"
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010018#include "system-proxy/sandboxed_worker.h"
Andreea Costinas942284d2020-01-28 16:28:40 +010019
20namespace system_proxy {
21namespace {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010022
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020023constexpr int kProxyPort = 3128;
Andreea Costinas77b180e2020-05-12 15:17:32 +020024constexpr char kNoCredentialsSpecifiedError[] =
25 "No authentication credentials specified";
26constexpr char kOnlySystemTrafficSupportedError[] =
27 "Only system services traffic is currenly supported";
28constexpr char kFailedToStartWorkerError[] = "Failed to start worker process";
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020029
Andreea Costinas942284d2020-01-28 16:28:40 +010030// Serializes |proto| to a vector of bytes.
31std::vector<uint8_t> SerializeProto(
32 const google::protobuf::MessageLite& proto) {
33 std::vector<uint8_t> proto_blob(proto.ByteSizeLong());
34 DCHECK(proto.SerializeToArray(proto_blob.data(), proto_blob.size()));
35 return proto_blob;
36}
37
38// Parses a proto from an array of bytes |proto_blob|. Returns
39// ERROR_PARSE_REQUEST_FAILED on error.
40std::string DeserializeProto(const base::Location& from_here,
41 google::protobuf::MessageLite* proto,
42 const std::vector<uint8_t>& proto_blob) {
43 if (!proto->ParseFromArray(proto_blob.data(), proto_blob.size())) {
44 const std::string error_message = "Failed to parse proto message.";
45 LOG(ERROR) << from_here.ToString() << error_message;
46 return error_message;
47 }
48 return "";
49}
50} // namespace
51
52SystemProxyAdaptor::SystemProxyAdaptor(
53 std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object)
54 : org::chromium::SystemProxyAdaptor(this),
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010055 dbus_object_(std::move(dbus_object)),
56 weak_ptr_factory_(this) {}
Andreea Costinas942284d2020-01-28 16:28:40 +010057
58SystemProxyAdaptor::~SystemProxyAdaptor() = default;
59
60void SystemProxyAdaptor::RegisterAsync(
61 const brillo::dbus_utils::AsyncEventSequencer::CompletionAction&
62 completion_callback) {
63 RegisterWithDBusObject(dbus_object_.get());
64 dbus_object_->RegisterAsync(completion_callback);
65}
66
Andreea Costinas77b180e2020-05-12 15:17:32 +020067std::vector<uint8_t> SystemProxyAdaptor::SetAuthenticationDetails(
68 const std::vector<uint8_t>& request_blob) {
69 LOG(INFO) << "Received set authentication details request.";
70
71 SetAuthenticationDetailsRequest request;
72 const std::string error_message =
73 DeserializeProto(FROM_HERE, &request, request_blob);
74
75 SetAuthenticationDetailsResponse response;
76 if (!error_message.empty()) {
77 response.set_error_message(error_message);
78 return SerializeProto(response);
79 }
80
81 if (!request.has_credentials() && !request.has_kerberos_enabled()) {
82 response.set_error_message(kNoCredentialsSpecifiedError);
83 return SerializeProto(response);
84 }
85
86 if (request.traffic_type() != TrafficOrigin::SYSTEM) {
87 response.set_error_message(kOnlySystemTrafficSupportedError);
88 return SerializeProto(response);
89 }
90
91 if (!CreateWorkerIfNeeded(/* user_traffic */ false)) {
92 response.set_error_message(kFailedToStartWorkerError);
93 return SerializeProto(response);
94 }
95
96 if (request.has_credentials()) {
97 if (!request.credentials().has_username() ||
98 !request.credentials().has_password()) {
99 response.set_error_message(kNoCredentialsSpecifiedError);
100 return SerializeProto(response);
101 }
102 brillo::MessageLoop::current()->PostTask(
103 FROM_HERE, base::Bind(&SystemProxyAdaptor::SetCredentialsTask,
104 weak_ptr_factory_.GetWeakPtr(),
105 system_services_worker_.get(),
106 request.credentials().username(),
107 request.credentials().password()));
108 }
109
110 return SerializeProto(response);
111}
112
Andreea Costinas942284d2020-01-28 16:28:40 +0100113std::vector<uint8_t> SystemProxyAdaptor::SetSystemTrafficCredentials(
114 const std::vector<uint8_t>& request_blob) {
115 LOG(INFO) << "Received set credentials request.";
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100116
Andreea Costinas942284d2020-01-28 16:28:40 +0100117 SetSystemTrafficCredentialsRequest request;
118 const std::string error_message =
119 DeserializeProto(FROM_HERE, &request, request_blob);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100120
Andreea Costinas942284d2020-01-28 16:28:40 +0100121 SetSystemTrafficCredentialsResponse response;
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100122 if (!error_message.empty()) {
Andreea Costinas942284d2020-01-28 16:28:40 +0100123 response.set_error_message(error_message);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100124 return SerializeProto(response);
125 }
126
127 if (!request.has_system_services_username() ||
128 !request.has_system_services_password()) {
Andreea Costinas77b180e2020-05-12 15:17:32 +0200129 response.set_error_message(kNoCredentialsSpecifiedError);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100130 return SerializeProto(response);
131 }
132
Andreea Costinas77b180e2020-05-12 15:17:32 +0200133 if (!CreateWorkerIfNeeded(/* user_traffic */ false)) {
134 response.set_error_message(kFailedToStartWorkerError);
135 return SerializeProto(response);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100136 }
137
138 brillo::MessageLoop::current()->PostTask(
139 FROM_HERE,
140 base::Bind(&SystemProxyAdaptor::SetCredentialsTask,
141 weak_ptr_factory_.GetWeakPtr(), system_services_worker_.get(),
142 request.system_services_username(),
143 request.system_services_password()));
144
Andreea Costinas942284d2020-01-28 16:28:40 +0100145 return SerializeProto(response);
146}
147
148std::vector<uint8_t> SystemProxyAdaptor::ShutDown() {
149 LOG(INFO) << "Received shutdown request.";
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100150
151 std::string error_message;
152 if (system_services_worker_ && system_services_worker_->IsRunning()) {
153 if (!system_services_worker_->Stop())
154 error_message =
155 "Failure to terminate worker process for system services traffic.";
156 }
157
158 if (arc_worker_ && arc_worker_->IsRunning()) {
159 if (!arc_worker_->Stop())
160 error_message += "Failure to terminate worker process for arc traffic.";
161 }
162
Andreea Costinas942284d2020-01-28 16:28:40 +0100163 ShutDownResponse response;
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100164 if (!error_message.empty())
165 response.set_error_message(error_message);
166
167 brillo::MessageLoop::current()->PostTask(
168 FROM_HERE, base::Bind(&SystemProxyAdaptor::ShutDownTask,
169 weak_ptr_factory_.GetWeakPtr()));
170
Andreea Costinas942284d2020-01-28 16:28:40 +0100171 return SerializeProto(response);
172}
173
Andreea Costinas5862b102020-03-19 14:45:36 +0100174void SystemProxyAdaptor::GetChromeProxyServersAsync(
175 const std::string& target_url,
176 const brillo::http::GetChromeProxyServersCallback& callback) {
Andreea Costinasc9defae2020-04-22 10:28:35 +0200177 brillo::http::GetChromeProxyServersAsync(dbus_object_->GetBus(), target_url,
178 move(callback));
Andreea Costinas5862b102020-03-19 14:45:36 +0100179}
180
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100181std::unique_ptr<SandboxedWorker> SystemProxyAdaptor::CreateWorker() {
Andreea Costinas5862b102020-03-19 14:45:36 +0100182 return std::make_unique<SandboxedWorker>(weak_ptr_factory_.GetWeakPtr());
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100183}
184
Andreea Costinas77b180e2020-05-12 15:17:32 +0200185bool SystemProxyAdaptor::CreateWorkerIfNeeded(bool user_traffic) {
186 if (user_traffic) {
187 // Not supported at the moment.
188 return false;
189 }
190 if (system_services_worker_) {
191 return true;
192 }
193
194 system_services_worker_ = CreateWorker();
195 if (!StartWorker(system_services_worker_.get(),
196 /* user_traffic= */ false)) {
197 system_services_worker_.reset();
198 return false;
199 }
200 // patchpanel_proxy is owned by |dbus_object_->bus_|.
201 dbus::ObjectProxy* patchpanel_proxy = dbus_object_->GetBus()->GetObjectProxy(
202 patchpanel::kPatchPanelServiceName,
203 dbus::ObjectPath(patchpanel::kPatchPanelServicePath));
204 patchpanel_proxy->WaitForServiceToBeAvailable(
205 base::Bind(&SystemProxyAdaptor::OnPatchpanelServiceAvailable,
206 weak_ptr_factory_.GetWeakPtr()));
207 return true;
208}
209
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100210void SystemProxyAdaptor::SetCredentialsTask(SandboxedWorker* worker,
211 const std::string& username,
212 const std::string& password) {
213 DCHECK(worker);
Andreea Costinas41e06442020-03-09 09:41:51 +0100214 worker->SetUsernameAndPassword(username, password);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100215}
216
217void SystemProxyAdaptor::ShutDownTask() {
218 brillo::MessageLoop::current()->BreakLoop();
219}
220
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200221bool SystemProxyAdaptor::StartWorker(SandboxedWorker* worker,
222 bool user_traffic) {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100223 DCHECK(worker);
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200224 return worker->Start();
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100225}
226
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200227// Called when the patchpanel D-Bus service becomes available.
228void SystemProxyAdaptor::OnPatchpanelServiceAvailable(bool is_available) {
229 if (!is_available) {
230 LOG(ERROR) << "Patchpanel service not available";
231 return;
232 }
233 if (system_services_worker_) {
234 DCHECK(system_services_worker_->IsRunning());
235 ConnectNamespace(system_services_worker_.get(), /* user_traffic= */ false);
236 }
237}
238
239bool SystemProxyAdaptor::ConnectNamespace(SandboxedWorker* worker,
240 bool user_traffic) {
241 std::unique_ptr<patchpanel::Client> patchpanel_client =
242 patchpanel::Client::New();
243 if (!patchpanel_client) {
244 LOG(ERROR) << "Failed to open networking service client";
245 return false;
246 }
247
248 std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse> result =
249 patchpanel_client->ConnectNamespace(
250 worker->pid(), "" /* outbound_ifname */, user_traffic);
251
252 if (!result.first.is_valid()) {
253 LOG(ERROR) << "Failed to setup network namespace";
254 return false;
255 }
256
257 worker->SetNetNamespaceLifelineFd(std::move(result.first));
Andreea Costinasa89309d2020-05-08 15:51:12 +0200258 if (!worker->SetListeningAddress(result.second.host_ipv4_address(),
259 kProxyPort)) {
260 return false;
261 }
262 OnNamespaceConnected(worker, user_traffic);
263 return true;
264}
265
266void SystemProxyAdaptor::OnNamespaceConnected(SandboxedWorker* worker,
267 bool user_traffic) {
268 WorkerActiveSignalDetails details;
269 details.set_traffic_origin(user_traffic ? TrafficOrigin::USER
270 : TrafficOrigin::SYSTEM);
271 details.set_local_proxy_url(worker->local_proxy_host_and_port());
272 SendWorkerActiveSignal(SerializeProto(details));
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100273}
274
Andreea Costinas942284d2020-01-28 16:28:40 +0100275} // namespace system_proxy