blob: 07d6920b061995adbb68870ba18c0b2fb91b1420 [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>
Andreea Costinas91f75352020-07-08 14:47:47 +020011#include <base/time/time.h>
Andreea Costinas942284d2020-01-28 16:28:40 +010012#include <brillo/dbus/dbus_object.h>
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010013#include <brillo/message_loops/message_loop.h>
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020014#include <chromeos/dbus/service_constants.h>
15#include <chromeos/patchpanel/client.h>
16#include <dbus/object_proxy.h>
Andreea Costinas942284d2020-01-28 16:28:40 +010017
Andreea Costinas922fbaf2020-05-28 11:55:22 +020018#include "system-proxy/kerberos_client.h"
Andreea Costinas942284d2020-01-28 16:28:40 +010019#include "system_proxy/proto_bindings/system_proxy_service.pb.h"
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010020#include "system-proxy/sandboxed_worker.h"
Andreea Costinas942284d2020-01-28 16:28:40 +010021
22namespace system_proxy {
23namespace {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010024
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020025constexpr int kProxyPort = 3128;
Andreea Costinas77b180e2020-05-12 15:17:32 +020026constexpr char kNoCredentialsSpecifiedError[] =
27 "No authentication credentials specified";
28constexpr char kOnlySystemTrafficSupportedError[] =
29 "Only system services traffic is currenly supported";
30constexpr char kFailedToStartWorkerError[] = "Failed to start worker process";
Andreea Costinas91f75352020-07-08 14:47:47 +020031// Time delay for calling patchpanel::ConnectNamespace(). Patchpanel needs to
32// enter the network namespace of the worker process to configure it and fails
33// if it's soon after the process starts. See https://crbug.com/1095170 for
34// details.
35constexpr base::TimeDelta kConnectNamespaceDelay =
36 base::TimeDelta::FromSeconds(1);
37constexpr int kNetworkNamespaceReconnectAttempts = 3;
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020038
Andreea Costinas942284d2020-01-28 16:28:40 +010039// Serializes |proto| to a vector of bytes.
40std::vector<uint8_t> SerializeProto(
41 const google::protobuf::MessageLite& proto) {
42 std::vector<uint8_t> proto_blob(proto.ByteSizeLong());
Andreea Costinasc991e232020-06-08 20:30:58 +020043 bool result = proto.SerializeToArray(proto_blob.data(), proto_blob.size());
44 DCHECK(result);
Andreea Costinas942284d2020-01-28 16:28:40 +010045 return proto_blob;
46}
47
48// Parses a proto from an array of bytes |proto_blob|. Returns
49// ERROR_PARSE_REQUEST_FAILED on error.
50std::string DeserializeProto(const base::Location& from_here,
51 google::protobuf::MessageLite* proto,
52 const std::vector<uint8_t>& proto_blob) {
53 if (!proto->ParseFromArray(proto_blob.data(), proto_blob.size())) {
54 const std::string error_message = "Failed to parse proto message.";
55 LOG(ERROR) << from_here.ToString() << error_message;
56 return error_message;
57 }
58 return "";
59}
60} // namespace
61
62SystemProxyAdaptor::SystemProxyAdaptor(
63 std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object)
64 : org::chromium::SystemProxyAdaptor(this),
Andreea Costinas91f75352020-07-08 14:47:47 +020065 netns_reconnect_attempts_available_(kNetworkNamespaceReconnectAttempts),
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010066 dbus_object_(std::move(dbus_object)),
Andreea Costinas922fbaf2020-05-28 11:55:22 +020067 weak_ptr_factory_(this) {
68 kerberos_client_ = std::make_unique<KerberosClient>(dbus_object_->GetBus());
69}
Andreea Costinas942284d2020-01-28 16:28:40 +010070
71SystemProxyAdaptor::~SystemProxyAdaptor() = default;
72
73void SystemProxyAdaptor::RegisterAsync(
74 const brillo::dbus_utils::AsyncEventSequencer::CompletionAction&
75 completion_callback) {
76 RegisterWithDBusObject(dbus_object_.get());
77 dbus_object_->RegisterAsync(completion_callback);
78}
79
Andreea Costinas77b180e2020-05-12 15:17:32 +020080std::vector<uint8_t> SystemProxyAdaptor::SetAuthenticationDetails(
81 const std::vector<uint8_t>& request_blob) {
82 LOG(INFO) << "Received set authentication details request.";
83
84 SetAuthenticationDetailsRequest request;
85 const std::string error_message =
86 DeserializeProto(FROM_HERE, &request, request_blob);
87
88 SetAuthenticationDetailsResponse response;
89 if (!error_message.empty()) {
90 response.set_error_message(error_message);
91 return SerializeProto(response);
92 }
93
Andreea Costinas77b180e2020-05-12 15:17:32 +020094 if (request.traffic_type() != TrafficOrigin::SYSTEM) {
95 response.set_error_message(kOnlySystemTrafficSupportedError);
96 return SerializeProto(response);
97 }
98
99 if (!CreateWorkerIfNeeded(/* user_traffic */ false)) {
100 response.set_error_message(kFailedToStartWorkerError);
101 return SerializeProto(response);
102 }
103
104 if (request.has_credentials()) {
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200105 if (!((request.credentials().has_username() &&
106 request.credentials().has_password()) ||
107 request.has_protection_space())) {
Andreea Costinas77b180e2020-05-12 15:17:32 +0200108 response.set_error_message(kNoCredentialsSpecifiedError);
109 return SerializeProto(response);
110 }
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200111 worker::Credentials credentials;
112 if (request.has_protection_space()) {
113 worker::ProtectionSpace protection_space;
114 protection_space.set_origin(request.protection_space().origin());
115 protection_space.set_scheme(request.protection_space().scheme());
116 protection_space.set_realm(request.protection_space().realm());
117 *credentials.mutable_protection_space() = protection_space;
118 }
119 if (request.credentials().has_username()) {
120 credentials.set_username(request.credentials().username());
121 credentials.set_password(request.credentials().password());
122 }
Andreea Costinas77b180e2020-05-12 15:17:32 +0200123 brillo::MessageLoop::current()->PostTask(
124 FROM_HERE, base::Bind(&SystemProxyAdaptor::SetCredentialsTask,
125 weak_ptr_factory_.GetWeakPtr(),
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200126 system_services_worker_.get(), credentials));
Andreea Costinas77b180e2020-05-12 15:17:32 +0200127 }
128
Andreea Costinas922fbaf2020-05-28 11:55:22 +0200129 if (request.has_kerberos_enabled()) {
130 std::string principal_name = request.has_active_principal_name()
131 ? request.active_principal_name()
132 : std::string();
133
134 brillo::MessageLoop::current()->PostTask(
135 FROM_HERE, base::Bind(&SystemProxyAdaptor::SetKerberosEnabledTask,
136 weak_ptr_factory_.GetWeakPtr(),
137 system_services_worker_.get(),
138 request.kerberos_enabled(), principal_name));
139 }
140
Andreea Costinas77b180e2020-05-12 15:17:32 +0200141 return SerializeProto(response);
142}
143
Andreea Costinas942284d2020-01-28 16:28:40 +0100144std::vector<uint8_t> SystemProxyAdaptor::SetSystemTrafficCredentials(
145 const std::vector<uint8_t>& request_blob) {
Andreea Costinas942284d2020-01-28 16:28:40 +0100146 SetSystemTrafficCredentialsResponse response;
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200147 response.set_error_message("Deprecated. Please use SetAuthenticationDetails");
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100148
Andreea Costinas942284d2020-01-28 16:28:40 +0100149 return SerializeProto(response);
150}
151
152std::vector<uint8_t> SystemProxyAdaptor::ShutDown() {
153 LOG(INFO) << "Received shutdown request.";
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100154
155 std::string error_message;
156 if (system_services_worker_ && system_services_worker_->IsRunning()) {
157 if (!system_services_worker_->Stop())
158 error_message =
159 "Failure to terminate worker process for system services traffic.";
160 }
161
162 if (arc_worker_ && arc_worker_->IsRunning()) {
163 if (!arc_worker_->Stop())
164 error_message += "Failure to terminate worker process for arc traffic.";
165 }
166
Andreea Costinas942284d2020-01-28 16:28:40 +0100167 ShutDownResponse response;
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100168 if (!error_message.empty())
169 response.set_error_message(error_message);
170
171 brillo::MessageLoop::current()->PostTask(
172 FROM_HERE, base::Bind(&SystemProxyAdaptor::ShutDownTask,
173 weak_ptr_factory_.GetWeakPtr()));
174
Andreea Costinas942284d2020-01-28 16:28:40 +0100175 return SerializeProto(response);
176}
177
Andreea Costinas5862b102020-03-19 14:45:36 +0100178void SystemProxyAdaptor::GetChromeProxyServersAsync(
179 const std::string& target_url,
180 const brillo::http::GetChromeProxyServersCallback& callback) {
Andreea Costinasc9defae2020-04-22 10:28:35 +0200181 brillo::http::GetChromeProxyServersAsync(dbus_object_->GetBus(), target_url,
182 move(callback));
Andreea Costinas5862b102020-03-19 14:45:36 +0100183}
184
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100185std::unique_ptr<SandboxedWorker> SystemProxyAdaptor::CreateWorker() {
Andreea Costinas5862b102020-03-19 14:45:36 +0100186 return std::make_unique<SandboxedWorker>(weak_ptr_factory_.GetWeakPtr());
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100187}
188
Andreea Costinas77b180e2020-05-12 15:17:32 +0200189bool SystemProxyAdaptor::CreateWorkerIfNeeded(bool user_traffic) {
190 if (user_traffic) {
191 // Not supported at the moment.
192 return false;
193 }
194 if (system_services_worker_) {
195 return true;
196 }
197
198 system_services_worker_ = CreateWorker();
199 if (!StartWorker(system_services_worker_.get(),
200 /* user_traffic= */ false)) {
201 system_services_worker_.reset();
202 return false;
203 }
204 // patchpanel_proxy is owned by |dbus_object_->bus_|.
205 dbus::ObjectProxy* patchpanel_proxy = dbus_object_->GetBus()->GetObjectProxy(
206 patchpanel::kPatchPanelServiceName,
207 dbus::ObjectPath(patchpanel::kPatchPanelServicePath));
208 patchpanel_proxy->WaitForServiceToBeAvailable(
209 base::Bind(&SystemProxyAdaptor::OnPatchpanelServiceAvailable,
210 weak_ptr_factory_.GetWeakPtr()));
211 return true;
212}
213
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200214void SystemProxyAdaptor::SetCredentialsTask(
215 SandboxedWorker* worker, const worker::Credentials& credentials) {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100216 DCHECK(worker);
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200217 worker->SetCredentials(credentials);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100218}
219
Andreea Costinas922fbaf2020-05-28 11:55:22 +0200220void SystemProxyAdaptor::SetKerberosEnabledTask(
221 SandboxedWorker* worker,
222 bool kerberos_enabled,
223 const std::string& principal_name) {
224 DCHECK(worker);
225
226 worker->SetKerberosEnabled(kerberos_enabled,
227 kerberos_client_->krb5_conf_path(),
228 kerberos_client_->krb5_ccache_path());
229 kerberos_client_->SetKerberosEnabled(kerberos_enabled);
230 if (kerberos_enabled) {
231 kerberos_client_->SetPrincipalName(principal_name);
232 }
233}
234
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100235void SystemProxyAdaptor::ShutDownTask() {
236 brillo::MessageLoop::current()->BreakLoop();
237}
238
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200239bool SystemProxyAdaptor::StartWorker(SandboxedWorker* worker,
240 bool user_traffic) {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100241 DCHECK(worker);
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200242 return worker->Start();
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100243}
244
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200245// Called when the patchpanel D-Bus service becomes available.
246void SystemProxyAdaptor::OnPatchpanelServiceAvailable(bool is_available) {
247 if (!is_available) {
248 LOG(ERROR) << "Patchpanel service not available";
249 return;
250 }
251 if (system_services_worker_) {
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200252 ConnectNamespace(system_services_worker_.get(), /* user_traffic= */ false);
253 }
254}
255
Andreea Costinas91f75352020-07-08 14:47:47 +0200256void SystemProxyAdaptor::ConnectNamespace(SandboxedWorker* worker,
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200257 bool user_traffic) {
Andreea Costinas91f75352020-07-08 14:47:47 +0200258 DCHECK(system_services_worker_->IsRunning());
259 DCHECK_GT(netns_reconnect_attempts_available_, 0);
260 --netns_reconnect_attempts_available_;
261 // TODO(b/160736881, acostinas): Remove the delay after patchpanel
262 // implements "ip netns" to create the veth pair across network namespaces.
263 brillo::MessageLoop::current()->PostDelayedTask(
264 FROM_HERE,
265 base::Bind(&SystemProxyAdaptor::ConnectNamespaceTask,
266 weak_ptr_factory_.GetWeakPtr(), worker,
267 /* user_traffic= */ false),
268 kConnectNamespaceDelay);
269}
270
271void SystemProxyAdaptor::ConnectNamespaceTask(SandboxedWorker* worker,
272 bool user_traffic) {
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200273 std::unique_ptr<patchpanel::Client> patchpanel_client =
274 patchpanel::Client::New();
275 if (!patchpanel_client) {
276 LOG(ERROR) << "Failed to open networking service client";
Andreea Costinas91f75352020-07-08 14:47:47 +0200277 return;
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200278 }
279
280 std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse> result =
281 patchpanel_client->ConnectNamespace(
282 worker->pid(), "" /* outbound_ifname */, user_traffic);
283
284 if (!result.first.is_valid()) {
Andreea Costinas91f75352020-07-08 14:47:47 +0200285 LOG(ERROR) << "Failed to setup network namespace on attempt "
286 << kNetworkNamespaceReconnectAttempts -
287 netns_reconnect_attempts_available_;
288 if (netns_reconnect_attempts_available_ > 0) {
289 ConnectNamespace(worker, user_traffic);
290 }
291 return;
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200292 }
293
294 worker->SetNetNamespaceLifelineFd(std::move(result.first));
Andreea Costinasa89309d2020-05-08 15:51:12 +0200295 if (!worker->SetListeningAddress(result.second.host_ipv4_address(),
296 kProxyPort)) {
Andreea Costinas91f75352020-07-08 14:47:47 +0200297 return;
Andreea Costinasa89309d2020-05-08 15:51:12 +0200298 }
299 OnNamespaceConnected(worker, user_traffic);
Andreea Costinasa89309d2020-05-08 15:51:12 +0200300}
301
302void SystemProxyAdaptor::OnNamespaceConnected(SandboxedWorker* worker,
303 bool user_traffic) {
304 WorkerActiveSignalDetails details;
305 details.set_traffic_origin(user_traffic ? TrafficOrigin::USER
306 : TrafficOrigin::SYSTEM);
307 details.set_local_proxy_url(worker->local_proxy_host_and_port());
308 SendWorkerActiveSignal(SerializeProto(details));
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100309}
310
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200311void SystemProxyAdaptor::RequestAuthenticationCredentials(
312 const worker::ProtectionSpace& protection_space) {
313 AuthenticationRequiredDetails details;
314 ProtectionSpace proxy_protection_space;
315 proxy_protection_space.set_origin(protection_space.origin());
316 proxy_protection_space.set_realm(protection_space.realm());
317 proxy_protection_space.set_scheme(protection_space.scheme());
318 *details.mutable_proxy_protection_space() = proxy_protection_space;
319 SendAuthenticationRequiredSignal(SerializeProto(details));
320}
321
Andreea Costinas942284d2020-01-28 16:28:40 +0100322} // namespace system_proxy