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, &parameter_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