blob: 46f88b77f94ae860868ed8ba267bcb11186f7d6c [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
Andreea Costinas922fbaf2020-05-28 11:55:22 +020017#include "system-proxy/kerberos_client.h"
Andreea Costinas942284d2020-01-28 16:28:40 +010018#include "system_proxy/proto_bindings/system_proxy_service.pb.h"
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010019#include "system-proxy/sandboxed_worker.h"
Andreea Costinas942284d2020-01-28 16:28:40 +010020
21namespace system_proxy {
22namespace {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010023
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020024constexpr int kProxyPort = 3128;
Andreea Costinas77b180e2020-05-12 15:17:32 +020025constexpr char kNoCredentialsSpecifiedError[] =
26 "No authentication credentials specified";
27constexpr char kOnlySystemTrafficSupportedError[] =
28 "Only system services traffic is currenly supported";
29constexpr char kFailedToStartWorkerError[] = "Failed to start worker process";
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020030
Andreea Costinas942284d2020-01-28 16:28:40 +010031// Serializes |proto| to a vector of bytes.
32std::vector<uint8_t> SerializeProto(
33 const google::protobuf::MessageLite& proto) {
34 std::vector<uint8_t> proto_blob(proto.ByteSizeLong());
Andreea Costinasc991e232020-06-08 20:30:58 +020035 bool result = proto.SerializeToArray(proto_blob.data(), proto_blob.size());
36 DCHECK(result);
Andreea Costinas942284d2020-01-28 16:28:40 +010037 return proto_blob;
38}
39
40// Parses a proto from an array of bytes |proto_blob|. Returns
41// ERROR_PARSE_REQUEST_FAILED on error.
42std::string DeserializeProto(const base::Location& from_here,
43 google::protobuf::MessageLite* proto,
44 const std::vector<uint8_t>& proto_blob) {
45 if (!proto->ParseFromArray(proto_blob.data(), proto_blob.size())) {
46 const std::string error_message = "Failed to parse proto message.";
47 LOG(ERROR) << from_here.ToString() << error_message;
48 return error_message;
49 }
50 return "";
51}
52} // namespace
53
54SystemProxyAdaptor::SystemProxyAdaptor(
55 std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object)
56 : org::chromium::SystemProxyAdaptor(this),
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010057 dbus_object_(std::move(dbus_object)),
Andreea Costinas922fbaf2020-05-28 11:55:22 +020058 weak_ptr_factory_(this) {
59 kerberos_client_ = std::make_unique<KerberosClient>(dbus_object_->GetBus());
60}
Andreea Costinas942284d2020-01-28 16:28:40 +010061
62SystemProxyAdaptor::~SystemProxyAdaptor() = default;
63
64void SystemProxyAdaptor::RegisterAsync(
65 const brillo::dbus_utils::AsyncEventSequencer::CompletionAction&
66 completion_callback) {
67 RegisterWithDBusObject(dbus_object_.get());
68 dbus_object_->RegisterAsync(completion_callback);
69}
70
Andreea Costinas77b180e2020-05-12 15:17:32 +020071std::vector<uint8_t> SystemProxyAdaptor::SetAuthenticationDetails(
72 const std::vector<uint8_t>& request_blob) {
73 LOG(INFO) << "Received set authentication details request.";
74
75 SetAuthenticationDetailsRequest request;
76 const std::string error_message =
77 DeserializeProto(FROM_HERE, &request, request_blob);
78
79 SetAuthenticationDetailsResponse response;
80 if (!error_message.empty()) {
81 response.set_error_message(error_message);
82 return SerializeProto(response);
83 }
84
85 if (!request.has_credentials() && !request.has_kerberos_enabled()) {
86 response.set_error_message(kNoCredentialsSpecifiedError);
87 return SerializeProto(response);
88 }
89
90 if (request.traffic_type() != TrafficOrigin::SYSTEM) {
91 response.set_error_message(kOnlySystemTrafficSupportedError);
92 return SerializeProto(response);
93 }
94
95 if (!CreateWorkerIfNeeded(/* user_traffic */ false)) {
96 response.set_error_message(kFailedToStartWorkerError);
97 return SerializeProto(response);
98 }
99
100 if (request.has_credentials()) {
101 if (!request.credentials().has_username() ||
102 !request.credentials().has_password()) {
103 response.set_error_message(kNoCredentialsSpecifiedError);
104 return SerializeProto(response);
105 }
106 brillo::MessageLoop::current()->PostTask(
107 FROM_HERE, base::Bind(&SystemProxyAdaptor::SetCredentialsTask,
108 weak_ptr_factory_.GetWeakPtr(),
109 system_services_worker_.get(),
110 request.credentials().username(),
111 request.credentials().password()));
112 }
113
Andreea Costinas922fbaf2020-05-28 11:55:22 +0200114 if (request.has_kerberos_enabled()) {
115 std::string principal_name = request.has_active_principal_name()
116 ? request.active_principal_name()
117 : std::string();
118
119 brillo::MessageLoop::current()->PostTask(
120 FROM_HERE, base::Bind(&SystemProxyAdaptor::SetKerberosEnabledTask,
121 weak_ptr_factory_.GetWeakPtr(),
122 system_services_worker_.get(),
123 request.kerberos_enabled(), principal_name));
124 }
125
Andreea Costinas77b180e2020-05-12 15:17:32 +0200126 return SerializeProto(response);
127}
128
Andreea Costinas942284d2020-01-28 16:28:40 +0100129std::vector<uint8_t> SystemProxyAdaptor::SetSystemTrafficCredentials(
130 const std::vector<uint8_t>& request_blob) {
131 LOG(INFO) << "Received set credentials request.";
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100132
Andreea Costinas942284d2020-01-28 16:28:40 +0100133 SetSystemTrafficCredentialsRequest request;
134 const std::string error_message =
135 DeserializeProto(FROM_HERE, &request, request_blob);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100136
Andreea Costinas942284d2020-01-28 16:28:40 +0100137 SetSystemTrafficCredentialsResponse response;
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100138 if (!error_message.empty()) {
Andreea Costinas942284d2020-01-28 16:28:40 +0100139 response.set_error_message(error_message);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100140 return SerializeProto(response);
141 }
142
143 if (!request.has_system_services_username() ||
144 !request.has_system_services_password()) {
Andreea Costinas77b180e2020-05-12 15:17:32 +0200145 response.set_error_message(kNoCredentialsSpecifiedError);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100146 return SerializeProto(response);
147 }
148
Andreea Costinas77b180e2020-05-12 15:17:32 +0200149 if (!CreateWorkerIfNeeded(/* user_traffic */ false)) {
150 response.set_error_message(kFailedToStartWorkerError);
151 return SerializeProto(response);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100152 }
153
154 brillo::MessageLoop::current()->PostTask(
155 FROM_HERE,
156 base::Bind(&SystemProxyAdaptor::SetCredentialsTask,
157 weak_ptr_factory_.GetWeakPtr(), system_services_worker_.get(),
158 request.system_services_username(),
159 request.system_services_password()));
160
Andreea Costinas942284d2020-01-28 16:28:40 +0100161 return SerializeProto(response);
162}
163
164std::vector<uint8_t> SystemProxyAdaptor::ShutDown() {
165 LOG(INFO) << "Received shutdown request.";
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100166
167 std::string error_message;
168 if (system_services_worker_ && system_services_worker_->IsRunning()) {
169 if (!system_services_worker_->Stop())
170 error_message =
171 "Failure to terminate worker process for system services traffic.";
172 }
173
174 if (arc_worker_ && arc_worker_->IsRunning()) {
175 if (!arc_worker_->Stop())
176 error_message += "Failure to terminate worker process for arc traffic.";
177 }
178
Andreea Costinas942284d2020-01-28 16:28:40 +0100179 ShutDownResponse response;
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100180 if (!error_message.empty())
181 response.set_error_message(error_message);
182
183 brillo::MessageLoop::current()->PostTask(
184 FROM_HERE, base::Bind(&SystemProxyAdaptor::ShutDownTask,
185 weak_ptr_factory_.GetWeakPtr()));
186
Andreea Costinas942284d2020-01-28 16:28:40 +0100187 return SerializeProto(response);
188}
189
Andreea Costinas5862b102020-03-19 14:45:36 +0100190void SystemProxyAdaptor::GetChromeProxyServersAsync(
191 const std::string& target_url,
192 const brillo::http::GetChromeProxyServersCallback& callback) {
Andreea Costinasc9defae2020-04-22 10:28:35 +0200193 brillo::http::GetChromeProxyServersAsync(dbus_object_->GetBus(), target_url,
194 move(callback));
Andreea Costinas5862b102020-03-19 14:45:36 +0100195}
196
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100197std::unique_ptr<SandboxedWorker> SystemProxyAdaptor::CreateWorker() {
Andreea Costinas5862b102020-03-19 14:45:36 +0100198 return std::make_unique<SandboxedWorker>(weak_ptr_factory_.GetWeakPtr());
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100199}
200
Andreea Costinas77b180e2020-05-12 15:17:32 +0200201bool SystemProxyAdaptor::CreateWorkerIfNeeded(bool user_traffic) {
202 if (user_traffic) {
203 // Not supported at the moment.
204 return false;
205 }
206 if (system_services_worker_) {
207 return true;
208 }
209
210 system_services_worker_ = CreateWorker();
211 if (!StartWorker(system_services_worker_.get(),
212 /* user_traffic= */ false)) {
213 system_services_worker_.reset();
214 return false;
215 }
216 // patchpanel_proxy is owned by |dbus_object_->bus_|.
217 dbus::ObjectProxy* patchpanel_proxy = dbus_object_->GetBus()->GetObjectProxy(
218 patchpanel::kPatchPanelServiceName,
219 dbus::ObjectPath(patchpanel::kPatchPanelServicePath));
220 patchpanel_proxy->WaitForServiceToBeAvailable(
221 base::Bind(&SystemProxyAdaptor::OnPatchpanelServiceAvailable,
222 weak_ptr_factory_.GetWeakPtr()));
223 return true;
224}
225
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100226void SystemProxyAdaptor::SetCredentialsTask(SandboxedWorker* worker,
227 const std::string& username,
228 const std::string& password) {
229 DCHECK(worker);
Andreea Costinas41e06442020-03-09 09:41:51 +0100230 worker->SetUsernameAndPassword(username, password);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100231}
232
Andreea Costinas922fbaf2020-05-28 11:55:22 +0200233void SystemProxyAdaptor::SetKerberosEnabledTask(
234 SandboxedWorker* worker,
235 bool kerberos_enabled,
236 const std::string& principal_name) {
237 DCHECK(worker);
238
239 worker->SetKerberosEnabled(kerberos_enabled,
240 kerberos_client_->krb5_conf_path(),
241 kerberos_client_->krb5_ccache_path());
242 kerberos_client_->SetKerberosEnabled(kerberos_enabled);
243 if (kerberos_enabled) {
244 kerberos_client_->SetPrincipalName(principal_name);
245 }
246}
247
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100248void SystemProxyAdaptor::ShutDownTask() {
249 brillo::MessageLoop::current()->BreakLoop();
250}
251
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200252bool SystemProxyAdaptor::StartWorker(SandboxedWorker* worker,
253 bool user_traffic) {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100254 DCHECK(worker);
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200255 return worker->Start();
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100256}
257
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200258// Called when the patchpanel D-Bus service becomes available.
259void SystemProxyAdaptor::OnPatchpanelServiceAvailable(bool is_available) {
260 if (!is_available) {
261 LOG(ERROR) << "Patchpanel service not available";
262 return;
263 }
264 if (system_services_worker_) {
265 DCHECK(system_services_worker_->IsRunning());
266 ConnectNamespace(system_services_worker_.get(), /* user_traffic= */ false);
267 }
268}
269
270bool SystemProxyAdaptor::ConnectNamespace(SandboxedWorker* worker,
271 bool user_traffic) {
272 std::unique_ptr<patchpanel::Client> patchpanel_client =
273 patchpanel::Client::New();
274 if (!patchpanel_client) {
275 LOG(ERROR) << "Failed to open networking service client";
276 return false;
277 }
278
279 std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse> result =
280 patchpanel_client->ConnectNamespace(
281 worker->pid(), "" /* outbound_ifname */, user_traffic);
282
283 if (!result.first.is_valid()) {
284 LOG(ERROR) << "Failed to setup network namespace";
285 return false;
286 }
287
288 worker->SetNetNamespaceLifelineFd(std::move(result.first));
Andreea Costinasa89309d2020-05-08 15:51:12 +0200289 if (!worker->SetListeningAddress(result.second.host_ipv4_address(),
290 kProxyPort)) {
291 return false;
292 }
293 OnNamespaceConnected(worker, user_traffic);
294 return true;
295}
296
297void SystemProxyAdaptor::OnNamespaceConnected(SandboxedWorker* worker,
298 bool user_traffic) {
299 WorkerActiveSignalDetails details;
300 details.set_traffic_origin(user_traffic ? TrafficOrigin::USER
301 : TrafficOrigin::SYSTEM);
302 details.set_local_proxy_url(worker->local_proxy_host_and_port());
303 SendWorkerActiveSignal(SerializeProto(details));
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100304}
305
Andreea Costinas942284d2020-01-28 16:28:40 +0100306} // namespace system_proxy