blob: 1427ea6adc5fbce09b2242e40b9231e03515f4c9 [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;
24
Andreea Costinas942284d2020-01-28 16:28:40 +010025// Serializes |proto| to a vector of bytes.
26std::vector<uint8_t> SerializeProto(
27 const google::protobuf::MessageLite& proto) {
28 std::vector<uint8_t> proto_blob(proto.ByteSizeLong());
29 DCHECK(proto.SerializeToArray(proto_blob.data(), proto_blob.size()));
30 return proto_blob;
31}
32
33// Parses a proto from an array of bytes |proto_blob|. Returns
34// ERROR_PARSE_REQUEST_FAILED on error.
35std::string DeserializeProto(const base::Location& from_here,
36 google::protobuf::MessageLite* proto,
37 const std::vector<uint8_t>& proto_blob) {
38 if (!proto->ParseFromArray(proto_blob.data(), proto_blob.size())) {
39 const std::string error_message = "Failed to parse proto message.";
40 LOG(ERROR) << from_here.ToString() << error_message;
41 return error_message;
42 }
43 return "";
44}
45} // namespace
46
47SystemProxyAdaptor::SystemProxyAdaptor(
48 std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object)
49 : org::chromium::SystemProxyAdaptor(this),
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010050 dbus_object_(std::move(dbus_object)),
51 weak_ptr_factory_(this) {}
Andreea Costinas942284d2020-01-28 16:28:40 +010052
53SystemProxyAdaptor::~SystemProxyAdaptor() = default;
54
55void SystemProxyAdaptor::RegisterAsync(
56 const brillo::dbus_utils::AsyncEventSequencer::CompletionAction&
57 completion_callback) {
58 RegisterWithDBusObject(dbus_object_.get());
59 dbus_object_->RegisterAsync(completion_callback);
60}
61
62std::vector<uint8_t> SystemProxyAdaptor::SetSystemTrafficCredentials(
63 const std::vector<uint8_t>& request_blob) {
64 LOG(INFO) << "Received set credentials request.";
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010065
Andreea Costinas942284d2020-01-28 16:28:40 +010066 SetSystemTrafficCredentialsRequest request;
67 const std::string error_message =
68 DeserializeProto(FROM_HERE, &request, request_blob);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010069
Andreea Costinas942284d2020-01-28 16:28:40 +010070 SetSystemTrafficCredentialsResponse response;
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010071 if (!error_message.empty()) {
Andreea Costinas942284d2020-01-28 16:28:40 +010072 response.set_error_message(error_message);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010073 return SerializeProto(response);
74 }
75
76 if (!request.has_system_services_username() ||
77 !request.has_system_services_password()) {
78 response.set_error_message("No credentials specified");
79 return SerializeProto(response);
80 }
81
82 if (!system_services_worker_) {
83 system_services_worker_ = CreateWorker();
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020084 if (!StartWorker(system_services_worker_.get(),
85 /* user_traffic= */ false)) {
Andreea Costinasc9defae2020-04-22 10:28:35 +020086 system_services_worker_.reset();
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020087
Andreea Costinasc9defae2020-04-22 10:28:35 +020088 response.set_error_message("Failed to start worker process");
89 return SerializeProto(response);
90 }
Andreea Costinasedb7c8e2020-04-22 10:58:04 +020091 // patchpanel_proxy is owned by |dbus_object_->bus_|.
92 dbus::ObjectProxy* patchpanel_proxy =
93 dbus_object_->GetBus()->GetObjectProxy(
94 patchpanel::kPatchPanelServiceName,
95 dbus::ObjectPath(patchpanel::kPatchPanelServicePath));
96 patchpanel_proxy->WaitForServiceToBeAvailable(
97 base::Bind(&SystemProxyAdaptor::OnPatchpanelServiceAvailable,
98 weak_ptr_factory_.GetWeakPtr()));
Andreea Costinasc7d5ad02020-03-09 09:41:51 +010099 }
100
101 brillo::MessageLoop::current()->PostTask(
102 FROM_HERE,
103 base::Bind(&SystemProxyAdaptor::SetCredentialsTask,
104 weak_ptr_factory_.GetWeakPtr(), system_services_worker_.get(),
105 request.system_services_username(),
106 request.system_services_password()));
107
Andreea Costinas942284d2020-01-28 16:28:40 +0100108 return SerializeProto(response);
109}
110
111std::vector<uint8_t> SystemProxyAdaptor::ShutDown() {
112 LOG(INFO) << "Received shutdown request.";
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100113
114 std::string error_message;
115 if (system_services_worker_ && system_services_worker_->IsRunning()) {
116 if (!system_services_worker_->Stop())
117 error_message =
118 "Failure to terminate worker process for system services traffic.";
119 }
120
121 if (arc_worker_ && arc_worker_->IsRunning()) {
122 if (!arc_worker_->Stop())
123 error_message += "Failure to terminate worker process for arc traffic.";
124 }
125
Andreea Costinas942284d2020-01-28 16:28:40 +0100126 ShutDownResponse response;
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100127 if (!error_message.empty())
128 response.set_error_message(error_message);
129
130 brillo::MessageLoop::current()->PostTask(
131 FROM_HERE, base::Bind(&SystemProxyAdaptor::ShutDownTask,
132 weak_ptr_factory_.GetWeakPtr()));
133
Andreea Costinas942284d2020-01-28 16:28:40 +0100134 return SerializeProto(response);
135}
136
Andreea Costinas5862b102020-03-19 14:45:36 +0100137void SystemProxyAdaptor::GetChromeProxyServersAsync(
138 const std::string& target_url,
139 const brillo::http::GetChromeProxyServersCallback& callback) {
Andreea Costinasc9defae2020-04-22 10:28:35 +0200140 brillo::http::GetChromeProxyServersAsync(dbus_object_->GetBus(), target_url,
141 move(callback));
Andreea Costinas5862b102020-03-19 14:45:36 +0100142}
143
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100144std::unique_ptr<SandboxedWorker> SystemProxyAdaptor::CreateWorker() {
Andreea Costinas5862b102020-03-19 14:45:36 +0100145 return std::make_unique<SandboxedWorker>(weak_ptr_factory_.GetWeakPtr());
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100146}
147
148void SystemProxyAdaptor::SetCredentialsTask(SandboxedWorker* worker,
149 const std::string& username,
150 const std::string& password) {
151 DCHECK(worker);
Andreea Costinas41e06442020-03-09 09:41:51 +0100152 worker->SetUsernameAndPassword(username, password);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100153}
154
155void SystemProxyAdaptor::ShutDownTask() {
156 brillo::MessageLoop::current()->BreakLoop();
157}
158
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200159bool SystemProxyAdaptor::StartWorker(SandboxedWorker* worker,
160 bool user_traffic) {
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100161 DCHECK(worker);
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200162 return worker->Start();
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100163}
164
Andreea Costinasedb7c8e2020-04-22 10:58:04 +0200165// Called when the patchpanel D-Bus service becomes available.
166void SystemProxyAdaptor::OnPatchpanelServiceAvailable(bool is_available) {
167 if (!is_available) {
168 LOG(ERROR) << "Patchpanel service not available";
169 return;
170 }
171 if (system_services_worker_) {
172 DCHECK(system_services_worker_->IsRunning());
173 ConnectNamespace(system_services_worker_.get(), /* user_traffic= */ false);
174 }
175}
176
177bool SystemProxyAdaptor::ConnectNamespace(SandboxedWorker* worker,
178 bool user_traffic) {
179 std::unique_ptr<patchpanel::Client> patchpanel_client =
180 patchpanel::Client::New();
181 if (!patchpanel_client) {
182 LOG(ERROR) << "Failed to open networking service client";
183 return false;
184 }
185
186 std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse> result =
187 patchpanel_client->ConnectNamespace(
188 worker->pid(), "" /* outbound_ifname */, user_traffic);
189
190 if (!result.first.is_valid()) {
191 LOG(ERROR) << "Failed to setup network namespace";
192 return false;
193 }
194
195 worker->SetNetNamespaceLifelineFd(std::move(result.first));
196 return worker->SetListeningAddress(result.second.peer_ipv4_address(),
197 kProxyPort);
Andreea Costinasc7d5ad02020-03-09 09:41:51 +0100198}
199
Andreea Costinas942284d2020-01-28 16:28:40 +0100200} // namespace system_proxy