1 // Copyright 2014 The Chromium 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
5 #include "extensions/browser/api/vpn_provider/vpn_provider_api.h"
6
7 #include <memory>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/values.h"
15 #include "extensions/browser/api/vpn_provider/vpn_service.h"
16 #include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
17 #include "extensions/common/api/vpn_provider.h"
18 #include "third_party/cros_system_api/dbus/service_constants.h"
19
20 namespace extensions {
21
22 namespace {
23
24 namespace api_vpn = extensions::api::vpn_provider;
25
26 const char kCIDRSeperator[] = "/";
27
CheckIPCIDRSanity(const std::string & value,bool cidr,bool ipv6)28 bool CheckIPCIDRSanity(const std::string& value, bool cidr, bool ipv6) {
29 int dots = ipv6 ? 0 : 3;
30 int sep = cidr ? 1 : 0;
31 int colon = ipv6 ? 7 : 0;
32 bool hex_allowed = ipv6;
33 int counter = 0;
34
35 for (const auto& elem : value) {
36 if (base::IsAsciiDigit(elem)) {
37 counter++;
38 continue;
39 }
40 if (elem == '.') {
41 if (!dots)
42 return false;
43 dots--;
44 } else if (elem == kCIDRSeperator[0]) {
45 if (!sep || dots || colon == 7 || !counter)
46 return false;
47 // Separator observed, no more dots and colons, only digits are allowed
48 // after observing separator. So setting hex_allowed to false.
49 sep--;
50 counter = 0;
51 colon = 0;
52 hex_allowed = false;
53 } else if (elem == ':') {
54 if (!colon)
55 return false;
56 colon--;
57 } else if (!hex_allowed || !base::IsHexDigit(elem)) {
58 return false;
59 } else {
60 counter++;
61 }
62 }
63 return !sep && !dots && (colon < 7) && counter;
64 }
65
CheckIPCIDRSanityList(const std::vector<std::string> & list,bool cidr,bool ipv6)66 bool CheckIPCIDRSanityList(const std::vector<std::string>& list,
67 bool cidr,
68 bool ipv6) {
69 for (const auto& address : list) {
70 if (!CheckIPCIDRSanity(address, cidr, ipv6)) {
71 return false;
72 }
73 }
74 return true;
75 }
76
ConvertParameters(const api_vpn::Parameters & parameters,base::DictionaryValue * parameter_value,std::string * error)77 void ConvertParameters(const api_vpn::Parameters& parameters,
78 base::DictionaryValue* parameter_value,
79 std::string* error) {
80 if (!CheckIPCIDRSanity(parameters.address, true /* CIDR */,
81 false /*IPV4 */)) {
82 *error = "Address CIDR sanity check failed.";
83 return;
84 }
85
86 if (!CheckIPCIDRSanityList(parameters.exclusion_list, true /* CIDR */,
87 false /*IPV4 */)) {
88 *error = "Exclusion list CIDR sanity check failed.";
89 return;
90 }
91
92 if (!CheckIPCIDRSanityList(parameters.inclusion_list, true /* CIDR */,
93 false /*IPV4 */)) {
94 *error = "Inclusion list CIDR sanity check failed.";
95 return;
96 }
97
98 if (!CheckIPCIDRSanityList(parameters.dns_servers, false /* Not CIDR */,
99 false /*IPV4 */)) {
100 *error = "DNS server IP sanity check failed.";
101 return;
102 }
103
104 std::vector<std::string> cidr_parts = base::SplitString(
105 parameters.address, kCIDRSeperator, base::KEEP_WHITESPACE,
106 base::SPLIT_WANT_NONEMPTY);
107 CHECK_EQ(2u, cidr_parts.size());
108
109 parameter_value->SetKey(shill::kAddressParameterThirdPartyVpn,
110 base::Value(cidr_parts[0]));
111
112 parameter_value->SetKey(shill::kSubnetPrefixParameterThirdPartyVpn,
113 base::Value(cidr_parts[1]));
114
115 std::string ip_delimiter(1, shill::kIPDelimiter);
116 parameter_value->SetKey(
117 shill::kExclusionListParameterThirdPartyVpn,
118 base::Value(base::JoinString(parameters.exclusion_list, ip_delimiter)));
119
120 parameter_value->SetKey(
121 shill::kInclusionListParameterThirdPartyVpn,
122 base::Value(base::JoinString(parameters.inclusion_list, ip_delimiter)));
123
124 if (parameters.mtu) {
125 parameter_value->SetKey(shill::kMtuParameterThirdPartyVpn,
126 base::Value(*parameters.mtu));
127 }
128
129 if (parameters.broadcast_address) {
130 parameter_value->SetKey(shill::kBroadcastAddressParameterThirdPartyVpn,
131 base::Value(*parameters.broadcast_address));
132 }
133
134 std::string non_ip_delimiter(1, shill::kNonIPDelimiter);
135 if (parameters.domain_search) {
136 parameter_value->SetKey(shill::kDomainSearchParameterThirdPartyVpn,
137 base::Value(base::JoinString(
138 *parameters.domain_search, non_ip_delimiter)));
139 }
140
141 parameter_value->SetKey(
142 shill::kDnsServersParameterThirdPartyVpn,
143 base::Value(base::JoinString(parameters.dns_servers, ip_delimiter)));
144
145 if (parameters.reconnect) {
146 parameter_value->SetKey(shill::kReconnectParameterThirdPartyVpn,
147 base::Value(*parameters.reconnect));
148 }
149
150 return;
151 }
152
153 } // namespace
154
~VpnThreadExtensionFunction()155 VpnThreadExtensionFunction::~VpnThreadExtensionFunction() {
156 }
157
SignalCallCompletionSuccess()158 void VpnThreadExtensionFunction::SignalCallCompletionSuccess() {
159 Respond(NoArguments());
160 }
161
SignalCallCompletionSuccessWithId(const std::string & configuration_id)162 void VpnThreadExtensionFunction::SignalCallCompletionSuccessWithId(
163 const std::string& configuration_id) {
164 Respond(OneArgument(std::make_unique<base::Value>(configuration_id)));
165 }
166
SignalCallCompletionSuccessWithWarning(const std::string & warning)167 void VpnThreadExtensionFunction::SignalCallCompletionSuccessWithWarning(
168 const std::string& warning) {
169 if (!warning.empty()) {
170 WriteToConsole(blink::mojom::ConsoleMessageLevel::kWarning, warning);
171 }
172 Respond(NoArguments());
173 }
174
SignalCallCompletionFailure(const std::string & error_name,const std::string & error_message)175 void VpnThreadExtensionFunction::SignalCallCompletionFailure(
176 const std::string& error_name,
177 const std::string& error_message) {
178 if (!error_name.empty() && !error_message.empty()) {
179 Respond(Error(error_name + ": " + error_message));
180 } else if (!error_name.empty()) {
181 Respond(Error(error_name));
182 } else {
183 Respond(Error(error_message));
184 }
185 }
186
~VpnProviderCreateConfigFunction()187 VpnProviderCreateConfigFunction::~VpnProviderCreateConfigFunction() {
188 }
189
Run()190 ExtensionFunction::ResponseAction VpnProviderCreateConfigFunction::Run() {
191 std::unique_ptr<api_vpn::CreateConfig::Params> params(
192 api_vpn::CreateConfig::Params::Create(*args_));
193 if (!params) {
194 return RespondNow(Error("Invalid arguments."));
195 }
196
197 chromeos::VpnService* service =
198 chromeos::VpnServiceFactory::GetForBrowserContext(browser_context());
199 if (!service) {
200 return RespondNow(Error("Invalid profile."));
201 }
202
203 // Use the configuration name as ID. In the future, a different ID scheme may
204 // be used, requiring a mapping between the two.
205 service->CreateConfiguration(
206 extension_id(), extension()->name(), params->name,
207 base::Bind(
208 &VpnProviderCreateConfigFunction::SignalCallCompletionSuccessWithId,
209 this, params->name),
210 base::Bind(&VpnProviderNotifyConnectionStateChangedFunction::
211 SignalCallCompletionFailure,
212 this));
213
214 return RespondLater();
215 }
216
~VpnProviderDestroyConfigFunction()217 VpnProviderDestroyConfigFunction::~VpnProviderDestroyConfigFunction() {
218 }
219
Run()220 ExtensionFunction::ResponseAction VpnProviderDestroyConfigFunction::Run() {
221 std::unique_ptr<api_vpn::DestroyConfig::Params> params(
222 api_vpn::DestroyConfig::Params::Create(*args_));
223 if (!params) {
224 return RespondNow(Error("Invalid arguments."));
225 }
226
227 chromeos::VpnService* service =
228 chromeos::VpnServiceFactory::GetForBrowserContext(browser_context());
229 if (!service) {
230 return RespondNow(Error("Invalid profile."));
231 }
232
233 service->DestroyConfiguration(
234 extension_id(), params->id,
235 base::Bind(&VpnProviderDestroyConfigFunction::SignalCallCompletionSuccess,
236 this),
237 base::Bind(&VpnProviderNotifyConnectionStateChangedFunction::
238 SignalCallCompletionFailure,
239 this));
240
241 return RespondLater();
242 }
243
~VpnProviderSetParametersFunction()244 VpnProviderSetParametersFunction::~VpnProviderSetParametersFunction() {
245 }
246
Run()247 ExtensionFunction::ResponseAction VpnProviderSetParametersFunction::Run() {
248 std::unique_ptr<api_vpn::SetParameters::Params> params(
249 api_vpn::SetParameters::Params::Create(*args_));
250 if (!params) {
251 return RespondNow(Error("Invalid arguments."));
252 }
253
254 chromeos::VpnService* service =
255 chromeos::VpnServiceFactory::GetForBrowserContext(browser_context());
256 if (!service) {
257 return RespondNow(Error("Invalid profile."));
258 }
259
260 base::DictionaryValue parameter_value;
261 std::string error;
262 ConvertParameters(params->parameters, ¶meter_value, &error);
263 if (!error.empty()) {
264 return RespondNow(Error(error));
265 }
266
267 service->SetParameters(
268 extension_id(), parameter_value,
269 base::Bind(&VpnProviderSetParametersFunction::
270 SignalCallCompletionSuccessWithWarning,
271 this),
272 base::Bind(&VpnProviderNotifyConnectionStateChangedFunction::
273 SignalCallCompletionFailure,
274 this));
275
276 return RespondLater();
277 }
278
~VpnProviderSendPacketFunction()279 VpnProviderSendPacketFunction::~VpnProviderSendPacketFunction() {
280 }
281
Run()282 ExtensionFunction::ResponseAction VpnProviderSendPacketFunction::Run() {
283 std::unique_ptr<api_vpn::SendPacket::Params> params(
284 api_vpn::SendPacket::Params::Create(*args_));
285 if (!params) {
286 return RespondNow(Error("Invalid arguments."));
287 }
288
289 chromeos::VpnService* service =
290 chromeos::VpnServiceFactory::GetForBrowserContext(browser_context());
291 if (!service) {
292 return RespondNow(Error("Invalid profile."));
293 }
294
295 service->SendPacket(
296 extension_id(),
297 std::vector<char>(params->data.begin(), params->data.end()),
298 base::Bind(&VpnProviderSendPacketFunction::SignalCallCompletionSuccess,
299 this),
300 base::Bind(&VpnProviderNotifyConnectionStateChangedFunction::
301 SignalCallCompletionFailure,
302 this));
303
304 return RespondLater();
305 }
306
307 VpnProviderNotifyConnectionStateChangedFunction::
~VpnProviderNotifyConnectionStateChangedFunction()308 ~VpnProviderNotifyConnectionStateChangedFunction() {
309 }
310
311 ExtensionFunction::ResponseAction
Run()312 VpnProviderNotifyConnectionStateChangedFunction::Run() {
313 std::unique_ptr<api_vpn::NotifyConnectionStateChanged::Params> params(
314 api_vpn::NotifyConnectionStateChanged::Params::Create(*args_));
315 if (!params) {
316 return RespondNow(Error("Invalid arguments."));
317 }
318
319 chromeos::VpnService* service =
320 chromeos::VpnServiceFactory::GetForBrowserContext(browser_context());
321 if (!service) {
322 return RespondNow(Error("Invalid profile."));
323 }
324
325 service->NotifyConnectionStateChanged(
326 extension_id(), params->state,
327 base::Bind(&VpnProviderNotifyConnectionStateChangedFunction::
328 SignalCallCompletionSuccess,
329 this),
330 base::Bind(&VpnProviderNotifyConnectionStateChangedFunction::
331 SignalCallCompletionFailure,
332 this));
333
334 return RespondLater();
335 }
336
337 } // namespace extensions
338