blob: 839cb313e8de78f1542412a0f433517c21d28b27 [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
Andreea Costinas77b180e2020-05-12 15:17:32 +020085 if (request.traffic_type() != TrafficOrigin::SYSTEM) {
86 response.set_error_message(kOnlySystemTrafficSupportedError);
87 return SerializeProto(response);
88 }
89
90 if (!CreateWorkerIfNeeded(/* user_traffic */ false)) {
91 response.set_error_message(kFailedToStartWorkerError);
92 return SerializeProto(response);
93 }
94
95 if (request.has_credentials()) {
Andreea Costinasdb2cbee2020-06-15 11:43:44 +020096 if (!((request.credentials().has_username() &&
97 request.credentials().has_password()) ||
98 request.has_protection_space())) {
Andreea Costinas77b180e2020-05-12 15:17:32 +020099 response.set_error_message(kNoCredentialsSpecifiedError);
100 return SerializeProto(response);
101 }
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200102 worker::Credentials credentials;
103 if (request.has_protection_space()) {
104 worker::ProtectionSpace protection_space;
105 protection_space.set_origin(request.protection_space().origin());
106 protection_space.set_scheme(request.protection_space().scheme());
107 protection_space.set_realm(request.protection_space().realm());
108 *credentials.mutable_protection_space() = protection_space;
109 }
110 if (request.credentials().has_username()) {
111 credentials.set_username(request.credentials().username());
112 credentials.set_password(request.credentials().password());
113 }
Andreea Costinas77b180e2020-05-12 15:17:32 +0200114 brillo::MessageLoop::current()->PostTask(
115 FROM_HERE, base::Bind(&SystemProxyAdaptor::SetCredentialsTask,
116 weak_ptr_factory_.GetWeakPtr(),
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200117 system_services_worker_.get(), credentials));
Andreea Costinas77b180e2020-05-12 15:17:32 +0200118 }
119
Andreea Costinas922fbaf2020-05-28 11:55:22 +0200120 if (request.has_kerberos_enabled()) {
121 std::string principal_name = request.has_active_principal_name()
122 ? request.active_principal_name()
123 : std::string();
124
125 brillo::MessageLoop::current()->PostTask(
126 FROM_HERE, base::Bind(&SystemProxyAdaptor::SetKerberosEnabledTask,
127 weak_ptr_factory_.GetWeakPtr(),
128 system_services_worker_.get(),
129 request.kerberos_enabled(), principal_name));
130 }
131
Andreea Costinas77b180e2020-05-12 15:17:32 +0200132 return SerializeProto(response);
133}
134
Andreea Costinas942284d2020-01-28 16:28:40 +0100135std::vector<uint8_t> SystemProxyAdaptor::SetSystemTrafficCredentials(
136 const std::vector<uint8_t>& request_blob) {
Andreea Costinas942284d2020-01-28 16:28:40 +0100137 SetSystemTrafficCredentialsResponse response;
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200138 response.set_error_message("Deprecated. Please use SetAuthenticationDetails");
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100139
Andreea Costinas942284d2020-01-28 16:28:40 +0100140 return SerializeProto(response);
141}
142
143std::vector<uint8_t> SystemProxyAdaptor::ShutDown() {
144 LOG(INFO) << "Received shutdown request.";
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100145
146 std::string error_message;
147 if (system_services_worker_ && system_services_worker_->IsRunning()) {
148 if (!system_services_worker_->Stop())
149 error_message =
150 "Failure to terminate worker process for system services traffic.";
151 }
152
153 if (arc_worker_ && arc_worker_->IsRunning()) {
154 if (!arc_worker_->Stop())
155 error_message += "Failure to terminate worker process for arc traffic.";
156 }
157
Andreea Costinas942284d2020-01-28 16:28:40 +0100158 ShutDownResponse response;
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100159 if (!error_message.empty())
160 response.set_error_message(error_message);
161
162 brillo::MessageLoop::current()->PostTask(
163 FROM_HERE, base::Bind(&SystemProxyAdaptor::ShutDownTask,
164 weak_ptr_factory_.GetWeakPtr()));
165
Andreea Costinas942284d2020-01-28 16:28:40 +0100166 return SerializeProto(response);
167}
168
Andreea Costinas5862b102020-03-19 14:45:36 +0100169void SystemProxyAdaptor::GetChromeProxyServersAsync(
170 const std::string& target_url,
171 const brillo::http::GetChromeProxyServersCallback& callback) {
Andreea Costinasc9defae2020-04-22 10:28:35 +0200172 brillo::http::GetChromeProxyServersAsync(dbus_object_->GetBus(), target_url,
173 move(callback));
Andreea Costinas5862b102020-03-19 14:45:36 +0100174}
175
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100176std::unique_ptr<SandboxedWorker> SystemProxyAdaptor::CreateWorker() {
Andreea Costinas5862b102020-03-19 14:45:36 +0100177 return std::make_unique<SandboxedWorker>(weak_ptr_factory_.GetWeakPtr());
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100178}
179
Andreea Costinas77b180e2020-05-12 15:17:32 +0200180bool SystemProxyAdaptor::CreateWorkerIfNeeded(bool user_traffic) {
181 if (user_traffic) {
182 // Not supported at the moment.
183 return false;
184 }
185 if (system_services_worker_) {
186 return true;
187 }
188
189 system_services_worker_ = CreateWorker();
190 if (!StartWorker(system_services_worker_.get(),
191 /* user_traffic= */ false)) {
192 system_services_worker_.reset();
193 return false;
194 }
195 // patchpanel_proxy is owned by |dbus_object_->bus_|.
196 dbus::ObjectProxy* patchpanel_proxy = dbus_object_->GetBus()->GetObjectProxy(
197 patchpanel::kPatchPanelServiceName,
198 dbus::ObjectPath(patchpanel::kPatchPanelServicePath));
199 patchpanel_proxy->WaitForServiceToBeAvailable(
200 base::Bind(&SystemProxyAdaptor::OnPatchpanelServiceAvailable,
201 weak_ptr_factory_.GetWeakPtr()));
202 return true;
203}
204
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200205void SystemProxyAdaptor::SetCredentialsTask(
206 SandboxedWorker* worker, const worker::Credentials& credentials) {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100207 DCHECK(worker);
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200208 worker->SetCredentials(credentials);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100209}
210
Andreea Costinas922fbaf2020-05-28 11:55:22 +0200211void SystemProxyAdaptor::SetKerberosEnabledTask(
212 SandboxedWorker* worker,
213 bool kerberos_enabled,
214 const std::string& principal_name) {
215 DCHECK(worker);
216
217 worker->SetKerberosEnabled(kerberos_enabled,
218 kerberos_client_->krb5_conf_path(),
219 kerberos_client_->krb5_ccache_path());
220 kerberos_client_->SetKerberosEnabled(kerberos_enabled);
221 if (kerberos_enabled) {
222 kerberos_client_->SetPrincipalName(principal_name);
223 }
224}
225
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100226void SystemProxyAdaptor::ShutDownTask() {
227 brillo::MessageLoop::current()->BreakLoop();
228}
229
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200230bool SystemProxyAdaptor::StartWorker(SandboxedWorker* worker,
231 bool user_traffic) {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100232 DCHECK(worker);
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200233 return worker->Start();
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100234}
235
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200236// Called when the patchpanel D-Bus service becomes available.
237void SystemProxyAdaptor::OnPatchpanelServiceAvailable(bool is_available) {
238 if (!is_available) {
239 LOG(ERROR) << "Patchpanel service not available";
240 return;
241 }
242 if (system_services_worker_) {
243 DCHECK(system_services_worker_->IsRunning());
244 ConnectNamespace(system_services_worker_.get(), /* user_traffic= */ false);
245 }
246}
247
248bool SystemProxyAdaptor::ConnectNamespace(SandboxedWorker* worker,
249 bool user_traffic) {
250 std::unique_ptr<patchpanel::Client> patchpanel_client =
251 patchpanel::Client::New();
252 if (!patchpanel_client) {
253 LOG(ERROR) << "Failed to open networking service client";
254 return false;
255 }
256
257 std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse> result =
258 patchpanel_client->ConnectNamespace(
259 worker->pid(), "" /* outbound_ifname */, user_traffic);
260
261 if (!result.first.is_valid()) {
262 LOG(ERROR) << "Failed to setup network namespace";
263 return false;
264 }
265
266 worker->SetNetNamespaceLifelineFd(std::move(result.first));
Andreea Costinasa89309d2020-05-08 15:51:12 +0200267 if (!worker->SetListeningAddress(result.second.host_ipv4_address(),
268 kProxyPort)) {
269 return false;
270 }
271 OnNamespaceConnected(worker, user_traffic);
272 return true;
273}
274
275void SystemProxyAdaptor::OnNamespaceConnected(SandboxedWorker* worker,
276 bool user_traffic) {
277 WorkerActiveSignalDetails details;
278 details.set_traffic_origin(user_traffic ? TrafficOrigin::USER
279 : TrafficOrigin::SYSTEM);
280 details.set_local_proxy_url(worker->local_proxy_host_and_port());
281 SendWorkerActiveSignal(SerializeProto(details));
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100282}
283
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200284void SystemProxyAdaptor::RequestAuthenticationCredentials(
285 const worker::ProtectionSpace& protection_space) {
286 AuthenticationRequiredDetails details;
287 ProtectionSpace proxy_protection_space;
288 proxy_protection_space.set_origin(protection_space.origin());
289 proxy_protection_space.set_realm(protection_space.realm());
290 proxy_protection_space.set_scheme(protection_space.scheme());
291 *details.mutable_proxy_protection_space() = proxy_protection_space;
292 SendAuthenticationRequiredSignal(SerializeProto(details));
293}
294
Andreea Costinas942284d2020-01-28 16:28:40 +0100295} // namespace system_proxy