blob: 34e46b3b4d926cd183e6d2efea4127884210f77f [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());
35 DCHECK(proto.SerializeToArray(proto_blob.data(), proto_blob.size()));
36 return proto_blob;
37}
38
39// Parses a proto from an array of bytes |proto_blob|. Returns
40// ERROR_PARSE_REQUEST_FAILED on error.
41std::string DeserializeProto(const base::Location& from_here,
42 google::protobuf::MessageLite* proto,
43 const std::vector<uint8_t>& proto_blob) {
44 if (!proto->ParseFromArray(proto_blob.data(), proto_blob.size())) {
45 const std::string error_message = "Failed to parse proto message.";
46 LOG(ERROR) << from_here.ToString() << error_message;
47 return error_message;
48 }
49 return "";
50}
51} // namespace
52
53SystemProxyAdaptor::SystemProxyAdaptor(
54 std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object)
55 : org::chromium::SystemProxyAdaptor(this),
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010056 dbus_object_(std::move(dbus_object)),
Andreea Costinas922fbaf2020-05-28 11:55:22 +020057 weak_ptr_factory_(this) {
58 kerberos_client_ = std::make_unique<KerberosClient>(dbus_object_->GetBus());
59}
Andreea Costinas942284d2020-01-28 16:28:40 +010060
61SystemProxyAdaptor::~SystemProxyAdaptor() = default;
62
63void SystemProxyAdaptor::RegisterAsync(
64 const brillo::dbus_utils::AsyncEventSequencer::CompletionAction&
65 completion_callback) {
66 RegisterWithDBusObject(dbus_object_.get());
67 dbus_object_->RegisterAsync(completion_callback);
68}
69
Andreea Costinas77b180e2020-05-12 15:17:32 +020070std::vector<uint8_t> SystemProxyAdaptor::SetAuthenticationDetails(
71 const std::vector<uint8_t>& request_blob) {
72 LOG(INFO) << "Received set authentication details request.";
73
74 SetAuthenticationDetailsRequest request;
75 const std::string error_message =
76 DeserializeProto(FROM_HERE, &request, request_blob);
77
78 SetAuthenticationDetailsResponse response;
79 if (!error_message.empty()) {
80 response.set_error_message(error_message);
81 return SerializeProto(response);
82 }
83
84 if (!request.has_credentials() && !request.has_kerberos_enabled()) {
85 response.set_error_message(kNoCredentialsSpecifiedError);
86 return SerializeProto(response);
87 }
88
89 if (request.traffic_type() != TrafficOrigin::SYSTEM) {
90 response.set_error_message(kOnlySystemTrafficSupportedError);
91 return SerializeProto(response);
92 }
93
94 if (!CreateWorkerIfNeeded(/* user_traffic */ false)) {
95 response.set_error_message(kFailedToStartWorkerError);
96 return SerializeProto(response);
97 }
98
99 if (request.has_credentials()) {
100 if (!request.credentials().has_username() ||
101 !request.credentials().has_password()) {
102 response.set_error_message(kNoCredentialsSpecifiedError);
103 return SerializeProto(response);
104 }
105 brillo::MessageLoop::current()->PostTask(
106 FROM_HERE, base::Bind(&SystemProxyAdaptor::SetCredentialsTask,
107 weak_ptr_factory_.GetWeakPtr(),
108 system_services_worker_.get(),
109 request.credentials().username(),
110 request.credentials().password()));
111 }
112
Andreea Costinas922fbaf2020-05-28 11:55:22 +0200113 if (request.has_kerberos_enabled()) {
114 std::string principal_name = request.has_active_principal_name()
115 ? request.active_principal_name()
116 : std::string();
117
118 brillo::MessageLoop::current()->PostTask(
119 FROM_HERE, base::Bind(&SystemProxyAdaptor::SetKerberosEnabledTask,
120 weak_ptr_factory_.GetWeakPtr(),
121 system_services_worker_.get(),
122 request.kerberos_enabled(), principal_name));
123 }
124
Andreea Costinas77b180e2020-05-12 15:17:32 +0200125 return SerializeProto(response);
126}
127
Andreea Costinas942284d2020-01-28 16:28:40 +0100128std::vector<uint8_t> SystemProxyAdaptor::SetSystemTrafficCredentials(
129 const std::vector<uint8_t>& request_blob) {
130 LOG(INFO) << "Received set credentials request.";
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100131
Andreea Costinas942284d2020-01-28 16:28:40 +0100132 SetSystemTrafficCredentialsRequest request;
133 const std::string error_message =
134 DeserializeProto(FROM_HERE, &request, request_blob);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100135
Andreea Costinas942284d2020-01-28 16:28:40 +0100136 SetSystemTrafficCredentialsResponse response;
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100137 if (!error_message.empty()) {
Andreea Costinas942284d2020-01-28 16:28:40 +0100138 response.set_error_message(error_message);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100139 return SerializeProto(response);
140 }
141
142 if (!request.has_system_services_username() ||
143 !request.has_system_services_password()) {
Andreea Costinas77b180e2020-05-12 15:17:32 +0200144 response.set_error_message(kNoCredentialsSpecifiedError);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100145 return SerializeProto(response);
146 }
147
Andreea Costinas77b180e2020-05-12 15:17:32 +0200148 if (!CreateWorkerIfNeeded(/* user_traffic */ false)) {
149 response.set_error_message(kFailedToStartWorkerError);
150 return SerializeProto(response);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100151 }
152
153 brillo::MessageLoop::current()->PostTask(
154 FROM_HERE,
155 base::Bind(&SystemProxyAdaptor::SetCredentialsTask,
156 weak_ptr_factory_.GetWeakPtr(), system_services_worker_.get(),
157 request.system_services_username(),
158 request.system_services_password()));
159
Andreea Costinas942284d2020-01-28 16:28:40 +0100160 return SerializeProto(response);
161}
162
163std::vector<uint8_t> SystemProxyAdaptor::ShutDown() {
164 LOG(INFO) << "Received shutdown request.";
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100165
166 std::string error_message;
167 if (system_services_worker_ && system_services_worker_->IsRunning()) {
168 if (!system_services_worker_->Stop())
169 error_message =
170 "Failure to terminate worker process for system services traffic.";
171 }
172
173 if (arc_worker_ && arc_worker_->IsRunning()) {
174 if (!arc_worker_->Stop())
175 error_message += "Failure to terminate worker process for arc traffic.";
176 }
177
Andreea Costinas942284d2020-01-28 16:28:40 +0100178 ShutDownResponse response;
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100179 if (!error_message.empty())
180 response.set_error_message(error_message);
181
182 brillo::MessageLoop::current()->PostTask(
183 FROM_HERE, base::Bind(&SystemProxyAdaptor::ShutDownTask,
184 weak_ptr_factory_.GetWeakPtr()));
185
Andreea Costinas942284d2020-01-28 16:28:40 +0100186 return SerializeProto(response);
187}
188
Andreea Costinas5862b102020-03-19 14:45:36 +0100189void SystemProxyAdaptor::GetChromeProxyServersAsync(
190 const std::string& target_url,
191 const brillo::http::GetChromeProxyServersCallback& callback) {
Andreea Costinasc9defae2020-04-22 10:28:35 +0200192 brillo::http::GetChromeProxyServersAsync(dbus_object_->GetBus(), target_url,
193 move(callback));
Andreea Costinas5862b102020-03-19 14:45:36 +0100194}
195
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100196std::unique_ptr<SandboxedWorker> SystemProxyAdaptor::CreateWorker() {
Andreea Costinas5862b102020-03-19 14:45:36 +0100197 return std::make_unique<SandboxedWorker>(weak_ptr_factory_.GetWeakPtr());
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100198}
199
Andreea Costinas77b180e2020-05-12 15:17:32 +0200200bool SystemProxyAdaptor::CreateWorkerIfNeeded(bool user_traffic) {
201 if (user_traffic) {
202 // Not supported at the moment.
203 return false;
204 }
205 if (system_services_worker_) {
206 return true;
207 }
208
209 system_services_worker_ = CreateWorker();
210 if (!StartWorker(system_services_worker_.get(),
211 /* user_traffic= */ false)) {
212 system_services_worker_.reset();
213 return false;
214 }
215 // patchpanel_proxy is owned by |dbus_object_->bus_|.
216 dbus::ObjectProxy* patchpanel_proxy = dbus_object_->GetBus()->GetObjectProxy(
217 patchpanel::kPatchPanelServiceName,
218 dbus::ObjectPath(patchpanel::kPatchPanelServicePath));
219 patchpanel_proxy->WaitForServiceToBeAvailable(
220 base::Bind(&SystemProxyAdaptor::OnPatchpanelServiceAvailable,
221 weak_ptr_factory_.GetWeakPtr()));
222 return true;
223}
224
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100225void SystemProxyAdaptor::SetCredentialsTask(SandboxedWorker* worker,
226 const std::string& username,
227 const std::string& password) {
228 DCHECK(worker);
Andreea Costinas41e06442020-03-09 09:41:51 +0100229 worker->SetUsernameAndPassword(username, password);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100230}
231
Andreea Costinas922fbaf2020-05-28 11:55:22 +0200232void SystemProxyAdaptor::SetKerberosEnabledTask(
233 SandboxedWorker* worker,
234 bool kerberos_enabled,
235 const std::string& principal_name) {
236 DCHECK(worker);
237
238 worker->SetKerberosEnabled(kerberos_enabled,
239 kerberos_client_->krb5_conf_path(),
240 kerberos_client_->krb5_ccache_path());
241 kerberos_client_->SetKerberosEnabled(kerberos_enabled);
242 if (kerberos_enabled) {
243 kerberos_client_->SetPrincipalName(principal_name);
244 }
245}
246
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100247void SystemProxyAdaptor::ShutDownTask() {
248 brillo::MessageLoop::current()->BreakLoop();
249}
250
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200251bool SystemProxyAdaptor::StartWorker(SandboxedWorker* worker,
252 bool user_traffic) {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100253 DCHECK(worker);
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200254 return worker->Start();
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100255}
256
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200257// Called when the patchpanel D-Bus service becomes available.
258void SystemProxyAdaptor::OnPatchpanelServiceAvailable(bool is_available) {
259 if (!is_available) {
260 LOG(ERROR) << "Patchpanel service not available";
261 return;
262 }
263 if (system_services_worker_) {
264 DCHECK(system_services_worker_->IsRunning());
265 ConnectNamespace(system_services_worker_.get(), /* user_traffic= */ false);
266 }
267}
268
269bool SystemProxyAdaptor::ConnectNamespace(SandboxedWorker* worker,
270 bool user_traffic) {
271 std::unique_ptr<patchpanel::Client> patchpanel_client =
272 patchpanel::Client::New();
273 if (!patchpanel_client) {
274 LOG(ERROR) << "Failed to open networking service client";
275 return false;
276 }
277
278 std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse> result =
279 patchpanel_client->ConnectNamespace(
280 worker->pid(), "" /* outbound_ifname */, user_traffic);
281
282 if (!result.first.is_valid()) {
283 LOG(ERROR) << "Failed to setup network namespace";
284 return false;
285 }
286
287 worker->SetNetNamespaceLifelineFd(std::move(result.first));
Andreea Costinasa89309d2020-05-08 15:51:12 +0200288 if (!worker->SetListeningAddress(result.second.host_ipv4_address(),
289 kProxyPort)) {
290 return false;
291 }
292 OnNamespaceConnected(worker, user_traffic);
293 return true;
294}
295
296void SystemProxyAdaptor::OnNamespaceConnected(SandboxedWorker* worker,
297 bool user_traffic) {
298 WorkerActiveSignalDetails details;
299 details.set_traffic_origin(user_traffic ? TrafficOrigin::USER
300 : TrafficOrigin::SYSTEM);
301 details.set_local_proxy_url(worker->local_proxy_host_and_port());
302 SendWorkerActiveSignal(SerializeProto(details));
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100303}
304
Andreea Costinas942284d2020-01-28 16:28:40 +0100305} // namespace system_proxy