1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "shill/vpn/openvpn_driver.h"
18 
19 #include <arpa/inet.h>
20 
21 #include <base/files/file_util.h>
22 #include <base/strings/string_number_conversions.h>
23 #include <base/strings/string_split.h>
24 #include <base/strings/string_util.h>
25 #if defined(__ANDROID__)
26 #include <dbus/service_constants.h>
27 #else
28 #include <chromeos/dbus/service_constants.h>
29 #endif  // __ANDROID__
30 
31 #include "shill/certificate_file.h"
32 #include "shill/connection.h"
33 #include "shill/device_info.h"
34 #include "shill/error.h"
35 #include "shill/ipconfig.h"
36 #include "shill/logging.h"
37 #include "shill/manager.h"
38 #include "shill/net/sockets.h"
39 #include "shill/process_manager.h"
40 #include "shill/rpc_task.h"
41 #include "shill/virtual_device.h"
42 #include "shill/vpn/openvpn_management_server.h"
43 #include "shill/vpn/vpn_service.h"
44 
45 using base::Closure;
46 using base::FilePath;
47 using base::SplitString;
48 using base::Unretained;
49 using base::WeakPtr;
50 using std::map;
51 using std::string;
52 using std::vector;
53 
54 namespace shill {
55 
56 namespace Logging {
57 static auto kModuleLogScope = ScopeLogger::kVPN;
ObjectID(const OpenVPNDriver * o)58 static string ObjectID(const OpenVPNDriver* o) {
59   return o->GetServiceRpcIdentifier();
60 }
61 }
62 
63 namespace {
64 
65 const char kChromeOSReleaseName[] = "CHROMEOS_RELEASE_NAME";
66 const char kChromeOSReleaseVersion[] = "CHROMEOS_RELEASE_VERSION";
67 const char kOpenVPNEnvVarPlatformName[] = "IV_PLAT";
68 const char kOpenVPNEnvVarPlatformVersion[] = "IV_PLAT_REL";
69 const char kOpenVPNForeignOptionPrefix[] = "foreign_option_";
70 const char kOpenVPNIfconfigBroadcast[] = "ifconfig_broadcast";
71 const char kOpenVPNIfconfigLocal[] = "ifconfig_local";
72 const char kOpenVPNIfconfigNetmask[] = "ifconfig_netmask";
73 const char kOpenVPNIfconfigRemote[] = "ifconfig_remote";
74 const char kOpenVPNRedirectGateway[] = "redirect_gateway";
75 const char kOpenVPNRedirectPrivate[] = "redirect_private";
76 const char kOpenVPNRouteOptionPrefix[] = "route_";
77 const char kOpenVPNRouteVPNGateway[] = "route_vpn_gateway";
78 const char kOpenVPNTrustedIP[] = "trusted_ip";
79 const char kOpenVPNTunMTU[] = "tun_mtu";
80 
81 const char kDefaultPKCS11Provider[] = "libchaps.so";
82 
83 // Some configurations pass the netmask in the ifconfig_remote property.
84 // This is due to some servers not explicitly indicating that they are using
85 // a "broadcast mode" network instead of peer-to-peer.  See
86 // http://crbug.com/241264 for an example of this issue.
87 const char kSuspectedNetmaskPrefix[] = "255.";
88 
DoNothingWithExitStatus(int exit_status)89 void DoNothingWithExitStatus(int exit_status) {
90 }
91 
92 }  // namespace
93 
94 // static
95 const char OpenVPNDriver::kDefaultCACertificates[] =
96     "/etc/ssl/certs/ca-certificates.crt";
97 // static
98 const char OpenVPNDriver::kOpenVPNPath[] = "/usr/sbin/openvpn";
99 // static
100 const char OpenVPNDriver::kOpenVPNScript[] = SHIMDIR "/openvpn-script";
101 // static
102 const VPNDriver::Property OpenVPNDriver::kProperties[] = {
103   { kOpenVPNAuthNoCacheProperty, 0 },
104   { kOpenVPNAuthProperty, 0 },
105   { kOpenVPNAuthRetryProperty, 0 },
106   { kOpenVPNAuthUserPassProperty, 0 },
107   { kOpenVPNCaCertNSSProperty, 0 },
108   { kOpenVPNCaCertProperty, 0 },
109   { kOpenVPNCipherProperty, 0 },
110   { kOpenVPNClientCertIdProperty, Property::kCredential },
111   { kOpenVPNCompLZOProperty, 0 },
112   { kOpenVPNCompNoAdaptProperty, 0 },
113   { kOpenVPNIgnoreDefaultRouteProperty, 0 },
114   { kOpenVPNKeyDirectionProperty, 0 },
115   { kOpenVPNNsCertTypeProperty, 0 },
116   { kOpenVPNOTPProperty,
117     Property::kEphemeral | Property::kCredential | Property::kWriteOnly },
118   { kOpenVPNPasswordProperty, Property::kCredential | Property::kWriteOnly },
119   { kOpenVPNPinProperty, Property::kCredential },
120   { kOpenVPNPortProperty, 0 },
121   { kOpenVPNProtoProperty, 0 },
122   { kOpenVPNProviderProperty, 0 },
123   { kOpenVPNPushPeerInfoProperty, 0 },
124   { kOpenVPNRemoteCertEKUProperty, 0 },
125   { kOpenVPNRemoteCertKUProperty, 0 },
126   { kOpenVPNRemoteCertTLSProperty, 0 },
127   { kOpenVPNRenegSecProperty, 0 },
128   { kOpenVPNServerPollTimeoutProperty, 0 },
129   { kOpenVPNShaperProperty, 0 },
130   { kOpenVPNStaticChallengeProperty, 0 },
131   { kOpenVPNTLSAuthContentsProperty, 0 },
132   { kOpenVPNTLSRemoteProperty, 0 },
133   { kOpenVPNTokenProperty,
134     Property::kEphemeral | Property::kCredential | Property::kWriteOnly },
135   { kOpenVPNUserProperty, 0 },
136   { kProviderHostProperty, 0 },
137   { kProviderTypeProperty, 0 },
138   { kOpenVPNCaCertPemProperty, Property::kArray },
139   { kOpenVPNCertProperty, 0 },
140   { kOpenVPNExtraCertPemProperty, Property::kArray },
141   { kOpenVPNKeyProperty, 0 },
142   { kOpenVPNPingExitProperty, 0 },
143   { kOpenVPNPingProperty, 0 },
144   { kOpenVPNPingRestartProperty, 0 },
145   { kOpenVPNTLSAuthProperty, 0 },
146   { kOpenVPNVerbProperty, 0 },
147   { kOpenVPNVerifyHashProperty, 0 },
148   { kOpenVPNVerifyX509NameProperty, 0 },
149   { kOpenVPNVerifyX509TypeProperty, 0 },
150   { kVPNMTUProperty, 0 },
151 };
152 
153 const char OpenVPNDriver::kLSBReleaseFile[] = "/etc/lsb-release";
154 
155 // Directory where OpenVPN configuration files are exported while the
156 // process is running.
157 const char OpenVPNDriver::kDefaultOpenVPNConfigurationDirectory[] =
158     RUNDIR "/openvpn_config";
159 
160 const int OpenVPNDriver::kReconnectOfflineTimeoutSeconds = 2 * 60;
161 const int OpenVPNDriver::kReconnectTLSErrorTimeoutSeconds = 20;
162 
OpenVPNDriver(ControlInterface * control,EventDispatcher * dispatcher,Metrics * metrics,Manager * manager,DeviceInfo * device_info,ProcessManager * process_manager)163 OpenVPNDriver::OpenVPNDriver(ControlInterface* control,
164                              EventDispatcher* dispatcher,
165                              Metrics* metrics,
166                              Manager* manager,
167                              DeviceInfo* device_info,
168                              ProcessManager* process_manager)
169     : VPNDriver(dispatcher, manager, kProperties, arraysize(kProperties)),
170       control_(control),
171       metrics_(metrics),
172       device_info_(device_info),
173       process_manager_(process_manager),
174       management_server_(new OpenVPNManagementServer(this)),
175       certificate_file_(new CertificateFile()),
176       extra_certificates_file_(new CertificateFile()),
177       lsb_release_file_(kLSBReleaseFile),
178       openvpn_config_directory_(kDefaultOpenVPNConfigurationDirectory),
179       pid_(0),
180       default_service_callback_tag_(0) {}
181 
~OpenVPNDriver()182 OpenVPNDriver::~OpenVPNDriver() {
183   IdleService();
184 }
185 
IdleService()186 void OpenVPNDriver::IdleService() {
187   Cleanup(Service::kStateIdle,
188           Service::kFailureUnknown,
189           Service::kErrorDetailsNone);
190 }
191 
FailService(Service::ConnectFailure failure,const string & error_details)192 void OpenVPNDriver::FailService(Service::ConnectFailure failure,
193                                 const string& error_details) {
194   Cleanup(Service::kStateFailure, failure, error_details);
195 }
196 
Cleanup(Service::ConnectState state,Service::ConnectFailure failure,const string & error_details)197 void OpenVPNDriver::Cleanup(Service::ConnectState state,
198                             Service::ConnectFailure failure,
199                             const string& error_details) {
200   SLOG(this, 2) << __func__ << "(" << Service::ConnectStateToString(state)
201                 << ", " << error_details << ")";
202   StopConnectTimeout();
203   // Disconnecting the management interface will terminate the openvpn
204   // process. Ensure this is handled robustly by first unregistering
205   // the callback for OnOpenVPNDied, and then terminating and reaping
206   // the process with StopProcess().
207   if (pid_) {
208     process_manager_->UpdateExitCallback(
209         pid_, base::Bind(DoNothingWithExitStatus));
210   }
211   management_server_->Stop();
212   if (!tls_auth_file_.empty()) {
213     base::DeleteFile(tls_auth_file_, false);
214     tls_auth_file_.clear();
215   }
216   if (!openvpn_config_file_.empty()) {
217     base::DeleteFile(openvpn_config_file_, false);
218     openvpn_config_file_.clear();
219   }
220   if (default_service_callback_tag_) {
221     manager()->DeregisterDefaultServiceCallback(default_service_callback_tag_);
222     default_service_callback_tag_ = 0;
223   }
224   rpc_task_.reset();
225   int interface_index = -1;
226   if (device_) {
227     interface_index = device_->interface_index();
228     device_->DropConnection();
229     device_->SetEnabled(false);
230     device_ = nullptr;
231   }
232   if (pid_) {
233     if (interface_index >= 0) {
234       // NB: |callback| must be bound to a static method, as
235       // |callback| may be called after our dtor completes.
236       const auto callback(
237           Bind(OnOpenVPNExited, device_info_->AsWeakPtr(), interface_index));
238       interface_index = -1;
239       process_manager_->UpdateExitCallback(pid_, callback);
240     }
241     process_manager_->StopProcess(pid_);
242     pid_ = 0;
243   }
244   if (interface_index >= 0) {
245     device_info_->DeleteInterface(interface_index);
246   }
247   tunnel_interface_.clear();
248   if (service_) {
249     if (state == Service::kStateFailure) {
250       service_->SetErrorDetails(error_details);
251       service_->SetFailure(failure);
252     } else {
253       service_->SetState(state);
254     }
255     service_ = nullptr;
256   }
257   ip_properties_ = IPConfig::Properties();
258 }
259 
260 // static
JoinOptions(const vector<vector<string>> & options,char separator)261 string OpenVPNDriver::JoinOptions(const vector<vector<string>>& options,
262                                   char separator) {
263   vector<string> option_strings;
264   for (const auto& option : options) {
265     vector<string> quoted_option;
266     for (const auto& argument : option) {
267       if (argument.find(' ') != string::npos ||
268           argument.find('\t') != string::npos ||
269           argument.find('"') != string::npos ||
270           argument.find(separator) != string::npos) {
271         string quoted_argument(argument);
272         const char separator_chars[] = { separator, '\0' };
273         base::ReplaceChars(argument, separator_chars, " ", &quoted_argument);
274         base::ReplaceChars(quoted_argument, "\\", "\\\\", &quoted_argument);
275         base::ReplaceChars(quoted_argument, "\"", "\\\"", &quoted_argument);
276         quoted_option.push_back("\"" + quoted_argument + "\"");
277       } else {
278         quoted_option.push_back(argument);
279       }
280     }
281     option_strings.push_back(base::JoinString(quoted_option, " "));
282   }
283   return base::JoinString(option_strings, string{separator});
284 }
285 
WriteConfigFile(const vector<vector<string>> & options,FilePath * config_file)286 bool OpenVPNDriver::WriteConfigFile(
287     const vector<vector<string>>& options,
288     FilePath* config_file) {
289   if (!base::DirectoryExists(openvpn_config_directory_)) {
290     if (!base::CreateDirectory(openvpn_config_directory_)) {
291       LOG(ERROR) << "Unable to create configuration directory  "
292                  << openvpn_config_directory_.value();
293       return false;
294     }
295     if (chmod(openvpn_config_directory_.value().c_str(), S_IRWXU)) {
296       LOG(ERROR) << "Failed to set permissions on "
297                  << openvpn_config_directory_.value();
298       base::DeleteFile(openvpn_config_directory_, true);
299       return false;
300     }
301   }
302 
303   string contents = JoinOptions(options, '\n');
304   contents.push_back('\n');
305   if (!base::CreateTemporaryFileInDir(openvpn_config_directory_, config_file) ||
306       base::WriteFile(*config_file, contents.data(), contents.size()) !=
307           static_cast<int>(contents.size())) {
308     LOG(ERROR) << "Unable to setup OpenVPN config file.";
309     return false;
310   }
311   return true;
312 }
313 
SpawnOpenVPN()314 bool OpenVPNDriver::SpawnOpenVPN() {
315   SLOG(this, 2) << __func__ << "(" << tunnel_interface_ << ")";
316 
317   vector<vector<string>> options;
318   Error error;
319   InitOptions(&options, &error);
320   if (error.IsFailure()) {
321     return false;
322   }
323   LOG(INFO) << "OpenVPN process options: " << JoinOptions(options, ',');
324   if (!WriteConfigFile(options, &openvpn_config_file_)) {
325     return false;
326   }
327 
328   // TODO(quiche): This should be migrated to use ExternalTask.
329   // (crbug.com/246263).
330   CHECK(!pid_);
331   pid_t pid = process_manager_->StartProcess(
332       FROM_HERE, FilePath(kOpenVPNPath),
333       vector<string>{"--config", openvpn_config_file_.value()},
334       GetEnvironment(),
335       false,  // Do not terminate with parent.
336       base::Bind(&OpenVPNDriver::OnOpenVPNDied, base::Unretained(this)));
337   if (pid < 0) {
338     LOG(ERROR) << "Unable to spawn: " << kOpenVPNPath;
339     return false;
340   }
341 
342   pid_ = pid;
343   return true;
344 }
345 
OnOpenVPNDied(int exit_status)346 void OpenVPNDriver::OnOpenVPNDied(int exit_status) {
347   SLOG(nullptr, 2) << __func__ << "(" << pid_ << ", "  << exit_status << ")";
348   pid_ = 0;
349   FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
350   // TODO(petkov): Figure if we need to restart the connection.
351 }
352 
353 // static
OnOpenVPNExited(const WeakPtr<DeviceInfo> & device_info,int interface_index,int)354 void OpenVPNDriver::OnOpenVPNExited(const WeakPtr<DeviceInfo>& device_info,
355                                     int interface_index,
356                                     int /* exit_status */) {
357   if (device_info) {
358     LOG(INFO) << "Deleting interface " << interface_index;
359     device_info->DeleteInterface(interface_index);
360   }
361 }
362 
ClaimInterface(const string & link_name,int interface_index)363 bool OpenVPNDriver::ClaimInterface(const string& link_name,
364                                    int interface_index) {
365   if (link_name != tunnel_interface_) {
366     return false;
367   }
368 
369   SLOG(this, 2) << "Claiming " << link_name << " for OpenVPN tunnel";
370 
371   CHECK(!device_);
372   device_ = new VirtualDevice(control_, dispatcher(), metrics_, manager(),
373                               link_name, interface_index, Technology::kVPN);
374   device_->SetEnabled(true);
375 
376   rpc_task_.reset(new RPCTask(control_, this));
377   if (SpawnOpenVPN()) {
378     default_service_callback_tag_ =
379         manager()->RegisterDefaultServiceCallback(
380             Bind(&OpenVPNDriver::OnDefaultServiceChanged, Unretained(this)));
381   } else {
382     FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
383   }
384   return true;
385 }
386 
GetLogin(string *,string *)387 void OpenVPNDriver::GetLogin(string* /*user*/, string* /*password*/) {
388   NOTREACHED();
389 }
390 
Notify(const string & reason,const map<string,string> & dict)391 void OpenVPNDriver::Notify(const string& reason,
392                            const map<string, string>& dict) {
393   LOG(INFO) << "IP configuration received: " << reason;
394   if (reason != "up") {
395     device_->DropConnection();
396     return;
397   }
398   // On restart/reconnect, update the existing IP configuration.
399   ParseIPConfiguration(dict, &ip_properties_);
400   device_->SelectService(service_);
401   device_->UpdateIPConfig(ip_properties_);
402   ReportConnectionMetrics();
403   StopConnectTimeout();
404 }
405 
ParseIPConfiguration(const map<string,string> & configuration,IPConfig::Properties * properties) const406 void OpenVPNDriver::ParseIPConfiguration(
407     const map<string, string>& configuration,
408     IPConfig::Properties* properties) const {
409   ForeignOptions foreign_options;
410   RouteOptions routes;
411   bool is_gateway_route_required = false;
412 
413   properties->address_family = IPAddress::kFamilyIPv4;
414   if (!properties->subnet_prefix) {
415     properties->subnet_prefix =
416         IPAddress::GetMaxPrefixLength(properties->address_family);
417   }
418   for (const auto& configuration_map : configuration) {
419     const string& key = configuration_map.first;
420     const string& value = configuration_map.second;
421     SLOG(this, 2) << "Processing: " << key << " -> " << value;
422     if (base::LowerCaseEqualsASCII(key, kOpenVPNIfconfigLocal)) {
423       properties->address = value;
424     } else if (base::LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
425       properties->broadcast_address = value;
426     } else if (base::LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
427       properties->subnet_prefix =
428           IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
429     } else if (base::LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
430       if (base::StartsWith(value, kSuspectedNetmaskPrefix,
431                            base::CompareCase::INSENSITIVE_ASCII)) {
432         LOG(WARNING) << "Option " << key << " value " << value
433                      << " looks more like a netmask than a peer address; "
434                      << "assuming it is the former.";
435         // In this situation, the "peer_address" value will be left
436         // unset and Connection::UpdateFromIPConfig() will treat the
437         // interface as if it were a broadcast-style network.  The
438         // kernel will, automatically set the peer address equal to
439         // the local address.
440         properties->subnet_prefix =
441             IPAddress::GetPrefixLengthFromMask(properties->address_family,
442                                                value);
443       } else {
444         properties->peer_address = value;
445       }
446     } else if (base::LowerCaseEqualsASCII(key, kOpenVPNRedirectGateway) ||
447                base::LowerCaseEqualsASCII(key, kOpenVPNRedirectPrivate)) {
448       is_gateway_route_required = true;
449     } else if (base::LowerCaseEqualsASCII(key, kOpenVPNRouteVPNGateway)) {
450       properties->gateway = value;
451     } else if (base::LowerCaseEqualsASCII(key, kOpenVPNTrustedIP)) {
452       size_t prefix = IPAddress::GetMaxPrefixLength(properties->address_family);
453       properties->exclusion_list.push_back(value + "/" +
454                                            base::SizeTToString(prefix));
455 
456     } else if (base::LowerCaseEqualsASCII(key, kOpenVPNTunMTU)) {
457       int mtu = 0;
458       if (base::StringToInt(value, &mtu) && mtu >= IPConfig::kMinIPv4MTU) {
459         properties->mtu = mtu;
460       } else {
461         LOG(ERROR) << "MTU " << value << " ignored.";
462       }
463     } else if (base::StartsWith(key, kOpenVPNForeignOptionPrefix,
464                                 base::CompareCase::INSENSITIVE_ASCII)) {
465       const string suffix = key.substr(strlen(kOpenVPNForeignOptionPrefix));
466       int order = 0;
467       if (base::StringToInt(suffix, &order)) {
468         foreign_options[order] = value;
469       } else {
470         LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
471       }
472     } else if (base::StartsWith(key, kOpenVPNRouteOptionPrefix,
473                                 base::CompareCase::INSENSITIVE_ASCII)) {
474       ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
475                        value, &routes);
476     } else {
477       SLOG(this, 2) << "Key ignored.";
478     }
479   }
480   ParseForeignOptions(foreign_options, properties);
481   SetRoutes(routes, properties);
482 
483   if (const_args()->ContainsString(kOpenVPNIgnoreDefaultRouteProperty)) {
484     if (is_gateway_route_required) {
485       LOG(INFO) << "Configuration request to ignore default route is "
486                 << "overridden by the remote server.";
487     } else {
488       SLOG(this, 2) << "Ignoring default route parameter as requested by "
489                     << "configuration.";
490       properties->gateway.clear();
491     }
492   }
493 }
494 
495 // static
ParseForeignOptions(const ForeignOptions & options,IPConfig::Properties * properties)496 void OpenVPNDriver::ParseForeignOptions(const ForeignOptions& options,
497                                         IPConfig::Properties* properties) {
498   vector<string> domain_search;
499   vector<string> dns_servers;
500   for (const auto& option_map : options) {
501     ParseForeignOption(option_map.second, &domain_search, &dns_servers);
502   }
503   if (!domain_search.empty()) {
504     properties->domain_search.swap(domain_search);
505   }
506   LOG_IF(WARNING, properties->domain_search.empty())
507       << "No search domains provided.";
508   if (!dns_servers.empty()) {
509     properties->dns_servers.swap(dns_servers);
510   }
511   LOG_IF(WARNING, properties->dns_servers.empty())
512       << "No DNS servers provided.";
513 }
514 
515 // static
ParseForeignOption(const string & option,vector<string> * domain_search,vector<string> * dns_servers)516 void OpenVPNDriver::ParseForeignOption(const string& option,
517                                        vector<string>* domain_search,
518                                        vector<string>* dns_servers) {
519   SLOG(nullptr, 2) << __func__ << "(" << option << ")";
520   vector<string> tokens = SplitString(option, " ", base::TRIM_WHITESPACE,
521                                       base::SPLIT_WANT_ALL);
522   if (tokens.size() != 3 ||
523       !base::LowerCaseEqualsASCII(tokens[0], "dhcp-option")) {
524     return;
525   }
526   if (base::LowerCaseEqualsASCII(tokens[1], "domain")) {
527     domain_search->push_back(tokens[2]);
528   } else if (base::LowerCaseEqualsASCII(tokens[1], "dns")) {
529     dns_servers->push_back(tokens[2]);
530   }
531 }
532 
533 // static
GetRouteOptionEntry(const string & prefix,const string & key,RouteOptions * routes)534 IPConfig::Route* OpenVPNDriver::GetRouteOptionEntry(
535     const string& prefix, const string& key, RouteOptions* routes) {
536   int order = 0;
537   if (!base::StartsWith(key, prefix, base::CompareCase::INSENSITIVE_ASCII) ||
538       !base::StringToInt(key.substr(prefix.size()), &order)) {
539     return nullptr;
540   }
541   return&(*routes)[order];
542 }
543 
544 // static
ParseRouteOption(const string & key,const string & value,RouteOptions * routes)545 void OpenVPNDriver::ParseRouteOption(
546     const string& key, const string& value, RouteOptions* routes) {
547   IPConfig::Route* route = GetRouteOptionEntry("network_", key, routes);
548   if (route) {
549     route->host = value;
550     return;
551   }
552   route = GetRouteOptionEntry("netmask_", key, routes);
553   if (route) {
554     route->netmask = value;
555     return;
556   }
557   route = GetRouteOptionEntry("gateway_", key, routes);
558   if (route) {
559     route->gateway = value;
560     return;
561   }
562   LOG(WARNING) << "Unknown route option ignored: " << key;
563 }
564 
565 // static
SetRoutes(const RouteOptions & routes,IPConfig::Properties * properties)566 void OpenVPNDriver::SetRoutes(const RouteOptions& routes,
567                               IPConfig::Properties* properties) {
568   vector<IPConfig::Route> new_routes;
569   for (const auto& route_map : routes) {
570     const IPConfig::Route& route = route_map.second;
571     if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
572       LOG(WARNING) << "Ignoring incomplete route: " << route_map.first;
573       continue;
574     }
575     new_routes.push_back(route);
576   }
577   if (!new_routes.empty()) {
578     properties->routes.swap(new_routes);
579   }
580   LOG_IF(WARNING, properties->routes.empty()) << "No routes provided.";
581 }
582 
583 // static
SplitPortFromHost(const string & host,string * name,string * port)584 bool OpenVPNDriver::SplitPortFromHost(
585     const string& host, string* name, string* port) {
586   vector<string> tokens = SplitString(host, ":", base::TRIM_WHITESPACE,
587                                       base::SPLIT_WANT_ALL);
588   int port_number = 0;
589   if (tokens.size() != 2 || tokens[0].empty() || tokens[1].empty() ||
590       !base::IsAsciiDigit(tokens[1][0]) ||
591       !base::StringToInt(tokens[1], &port_number) ||
592       port_number > std::numeric_limits<uint16_t>::max()) {
593     return false;
594   }
595   *name = tokens[0];
596   *port = tokens[1];
597   return true;
598 }
599 
Connect(const VPNServiceRefPtr & service,Error * error)600 void OpenVPNDriver::Connect(const VPNServiceRefPtr& service, Error* error) {
601   StartConnectTimeout(kDefaultConnectTimeoutSeconds);
602   service_ = service;
603   service_->SetState(Service::kStateConfiguring);
604   if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
605     Error::PopulateAndLog(
606         FROM_HERE, error, Error::kInternalError,
607         "Could not create tunnel interface.");
608     FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
609   }
610   // Wait for the ClaimInterface callback to continue the connection process.
611 }
612 
InitOptions(vector<vector<string>> * options,Error * error)613 void OpenVPNDriver::InitOptions(vector<vector<string>>* options, Error* error) {
614   string vpnhost = args()->LookupString(kProviderHostProperty, "");
615   if (vpnhost.empty()) {
616     Error::PopulateAndLog(
617         FROM_HERE, error, Error::kInvalidArguments, "VPN host not specified.");
618     return;
619   }
620   AppendOption("client", options);
621   AppendOption("tls-client", options);
622 
623   string host_name, host_port;
624   if (SplitPortFromHost(vpnhost, &host_name, &host_port)) {
625     DCHECK(!host_name.empty());
626     DCHECK(!host_port.empty());
627     AppendOption("remote", host_name, host_port, options);
628   } else {
629     AppendOption("remote", vpnhost, options);
630   }
631 
632   AppendOption("nobind", options);
633   AppendOption("persist-key", options);
634   AppendOption("persist-tun", options);
635 
636   CHECK(!tunnel_interface_.empty());
637   AppendOption("dev", tunnel_interface_, options);
638   AppendOption("dev-type", "tun", options);
639 
640   InitLoggingOptions(options);
641 
642   AppendValueOption(kVPNMTUProperty, "mtu", options);
643   AppendValueOption(kOpenVPNProtoProperty, "proto", options);
644   AppendValueOption(kOpenVPNPortProperty, "port", options);
645   AppendValueOption(kOpenVPNTLSAuthProperty, "tls-auth", options);
646   {
647     string contents =
648         args()->LookupString(kOpenVPNTLSAuthContentsProperty, "");
649     if (!contents.empty()) {
650       if (!base::CreateTemporaryFile(&tls_auth_file_) ||
651           base::WriteFile(tls_auth_file_, contents.data(), contents.size()) !=
652               static_cast<int>(contents.size())) {
653         Error::PopulateAndLog(
654             FROM_HERE, error, Error::kInternalError,
655             "Unable to setup tls-auth file.");
656         return;
657       }
658       AppendOption("tls-auth", tls_auth_file_.value(), options);
659     }
660   }
661   AppendValueOption(kOpenVPNTLSRemoteProperty, "tls-remote", options);
662   AppendValueOption(kOpenVPNCipherProperty, "cipher", options);
663   AppendValueOption(kOpenVPNAuthProperty, "auth", options);
664   AppendFlag(kOpenVPNAuthNoCacheProperty, "auth-nocache", options);
665   AppendValueOption(kOpenVPNAuthRetryProperty, "auth-retry", options);
666   AppendFlag(kOpenVPNCompLZOProperty, "comp-lzo", options);
667   AppendFlag(kOpenVPNCompNoAdaptProperty, "comp-noadapt", options);
668   AppendFlag(kOpenVPNPushPeerInfoProperty, "push-peer-info", options);
669   AppendValueOption(kOpenVPNRenegSecProperty, "reneg-sec", options);
670   AppendValueOption(kOpenVPNShaperProperty, "shaper", options);
671   AppendValueOption(kOpenVPNServerPollTimeoutProperty,
672                     "server-poll-timeout", options);
673 
674   if (!InitCAOptions(options, error)) {
675     return;
676   }
677 
678   // Additional remote certificate verification options.
679   InitCertificateVerifyOptions(options);
680   if (!InitExtraCertOptions(options, error)) {
681     return;
682   }
683 
684   // Client-side ping support.
685   AppendValueOption(kOpenVPNPingProperty, "ping", options);
686   AppendValueOption(kOpenVPNPingExitProperty, "ping-exit", options);
687   AppendValueOption(kOpenVPNPingRestartProperty, "ping-restart", options);
688 
689   AppendValueOption(kOpenVPNNsCertTypeProperty, "ns-cert-type", options);
690 
691   InitClientAuthOptions(options);
692   InitPKCS11Options(options);
693 
694   // TLS suport.
695   string remote_cert_tls =
696       args()->LookupString(kOpenVPNRemoteCertTLSProperty, "");
697   if (remote_cert_tls.empty()) {
698     remote_cert_tls = "server";
699   }
700   if (remote_cert_tls != "none") {
701     AppendOption("remote-cert-tls", remote_cert_tls, options);
702   }
703 
704   // This is an undocumented command line argument that works like a .cfg file
705   // entry. TODO(sleffler): Maybe roll this into the "tls-auth" option?
706   AppendValueOption(kOpenVPNKeyDirectionProperty, "key-direction", options);
707   AppendValueOption(kOpenVPNRemoteCertEKUProperty, "remote-cert-eku", options);
708   AppendDelimitedValueOption(kOpenVPNRemoteCertKUProperty,
709                              "remote-cert-ku", ' ', options);
710 
711   if (!InitManagementChannelOptions(options, error)) {
712     return;
713   }
714 
715   // Setup openvpn-script options and RPC information required to send back
716   // Layer 3 configuration.
717   AppendOption("setenv", kRPCTaskServiceVariable,
718                rpc_task_->GetRpcConnectionIdentifier(), options);
719   AppendOption("setenv", kRPCTaskServiceVariable,
720                rpc_task_->GetRpcConnectionIdentifier(), options);
721   AppendOption("setenv", kRPCTaskPathVariable, rpc_task_->GetRpcIdentifier(),
722                options);
723   AppendOption("script-security", "2", options);
724   AppendOption("up", kOpenVPNScript, options);
725   AppendOption("up-restart", options);
726 
727   // Disable openvpn handling since we do route+ifconfig work.
728   AppendOption("route-noexec", options);
729   AppendOption("ifconfig-noexec", options);
730 
731   // Drop root privileges on connection and enable callback scripts to send
732   // notify messages.
733   AppendOption("user", "openvpn", options);
734   AppendOption("group", "openvpn", options);
735 }
736 
InitCAOptions(vector<vector<string>> * options,Error * error)737 bool OpenVPNDriver::InitCAOptions(
738     vector<vector<string>>* options, Error* error) {
739   string ca_cert =
740       args()->LookupString(kOpenVPNCaCertProperty, "");
741   vector<string> ca_cert_pem;
742   if (args()->ContainsStrings(kOpenVPNCaCertPemProperty)) {
743     ca_cert_pem = args()->GetStrings(kOpenVPNCaCertPemProperty);
744   }
745 
746   int num_ca_cert_types = 0;
747   if (!ca_cert.empty())
748       num_ca_cert_types++;
749   if (!ca_cert_pem.empty())
750       num_ca_cert_types++;
751   if (num_ca_cert_types == 0) {
752     // Use default CAs if no CA certificate is provided.
753     AppendOption("ca", kDefaultCACertificates, options);
754     return true;
755   } else if (num_ca_cert_types > 1) {
756     Error::PopulateAndLog(
757         FROM_HERE, error, Error::kInvalidArguments,
758         "Can't specify more than one of CACert and CACertPEM.");
759     return false;
760   }
761   string cert_file;
762   if (!ca_cert_pem.empty()) {
763     DCHECK(ca_cert.empty());
764     FilePath certfile = certificate_file_->CreatePEMFromStrings(ca_cert_pem);
765     if (certfile.empty()) {
766       Error::PopulateAndLog(
767           FROM_HERE,
768           error,
769           Error::kInvalidArguments,
770           "Unable to extract PEM CA certificates.");
771       return false;
772     }
773     AppendOption("ca", certfile.value(), options);
774     return true;
775   }
776   DCHECK(!ca_cert.empty() && ca_cert_pem.empty());
777   AppendOption("ca", ca_cert, options);
778   return true;
779 }
780 
InitCertificateVerifyOptions(std::vector<std::vector<std::string>> * options)781 void OpenVPNDriver::InitCertificateVerifyOptions(
782     std::vector<std::vector<std::string>>* options) {
783   AppendValueOption(kOpenVPNVerifyHashProperty, "verify-hash", options);
784   string x509_name = args()->LookupString(kOpenVPNVerifyX509NameProperty, "");
785   if (!x509_name.empty()) {
786     string x509_type = args()->LookupString(kOpenVPNVerifyX509TypeProperty, "");
787     if (x509_type.empty()) {
788       AppendOption("verify-x509-name", x509_name, options);
789     } else {
790       AppendOption("verify-x509-name", x509_name, x509_type, options);
791     }
792   }
793 }
794 
InitExtraCertOptions(vector<vector<string>> * options,Error * error)795 bool OpenVPNDriver::InitExtraCertOptions(
796     vector<vector<string>>* options, Error* error) {
797   if (!args()->ContainsStrings(kOpenVPNExtraCertPemProperty)) {
798     // It's okay for this parameter to be unspecified.
799     return true;
800   }
801 
802   vector<string> extra_certs = args()->GetStrings(kOpenVPNExtraCertPemProperty);
803   if (extra_certs.empty()) {
804     // It's okay for this parameter to be empty.
805     return true;
806   }
807 
808   FilePath certfile =
809       extra_certificates_file_->CreatePEMFromStrings(extra_certs);
810   if (certfile.empty()) {
811     Error::PopulateAndLog(
812         FROM_HERE,
813         error,
814         Error::kInvalidArguments,
815         "Unable to extract extra PEM CA certificates.");
816     return false;
817   }
818 
819   AppendOption("extra-certs", certfile.value(), options);
820   return true;
821 }
822 
InitPKCS11Options(vector<vector<string>> * options)823 void OpenVPNDriver::InitPKCS11Options(vector<vector<string>>* options) {
824   string id = args()->LookupString(kOpenVPNClientCertIdProperty, "");
825   if (!id.empty()) {
826     string provider =
827         args()->LookupString(kOpenVPNProviderProperty, "");
828     if (provider.empty()) {
829       provider = kDefaultPKCS11Provider;
830     }
831     AppendOption("pkcs11-providers", provider, options);
832     AppendOption("pkcs11-id", id, options);
833   }
834 }
835 
InitClientAuthOptions(vector<vector<string>> * options)836 void OpenVPNDriver::InitClientAuthOptions(vector<vector<string>>* options) {
837   bool has_cert = AppendValueOption(kOpenVPNCertProperty, "cert", options) ||
838       !args()->LookupString(kOpenVPNClientCertIdProperty, "").empty();
839   bool has_key = AppendValueOption(kOpenVPNKeyProperty, "key", options);
840   // If the AuthUserPass property is set, or the User property is non-empty, or
841   // there's neither a key, nor a cert available, specify user-password client
842   // authentication.
843   if (args()->ContainsString(kOpenVPNAuthUserPassProperty) ||
844       !args()->LookupString(kOpenVPNUserProperty, "").empty() ||
845       (!has_cert && !has_key)) {
846     AppendOption("auth-user-pass", options);
847   }
848 }
849 
InitManagementChannelOptions(vector<vector<string>> * options,Error * error)850 bool OpenVPNDriver::InitManagementChannelOptions(
851     vector<vector<string>>* options, Error* error) {
852   if (!management_server_->Start(dispatcher(), &sockets_, options)) {
853     Error::PopulateAndLog(
854         FROM_HERE, error, Error::kInternalError,
855         "Unable to setup management channel.");
856     return false;
857   }
858   // If there's a connected default service already, allow the openvpn client to
859   // establish connection as soon as it's started. Otherwise, hold the client
860   // until an underlying service connects and OnDefaultServiceChanged is
861   // invoked.
862   if (manager()->IsConnected()) {
863     management_server_->ReleaseHold();
864   }
865   return true;
866 }
867 
InitLoggingOptions(vector<vector<string>> * options)868 void OpenVPNDriver::InitLoggingOptions(vector<vector<string>>* options) {
869   AppendOption("syslog", options);
870 
871   string verb = args()->LookupString(kOpenVPNVerbProperty, "");
872   if (verb.empty() && SLOG_IS_ON(VPN, 0)) {
873     verb = "3";
874   }
875   if (!verb.empty()) {
876     AppendOption("verb", verb, options);
877   }
878 }
879 
AppendOption(const string & option,vector<vector<string>> * options)880 void OpenVPNDriver::AppendOption(
881     const string& option, vector<vector<string>>* options) {
882   options->push_back(vector<string>{ option });
883 }
884 
AppendOption(const string & option,const string & value,vector<vector<string>> * options)885 void OpenVPNDriver::AppendOption(
886     const string& option,
887     const string& value,
888     vector<vector<string>>* options) {
889   options->push_back(vector<string>{ option, value });
890 }
891 
AppendOption(const string & option,const string & value0,const string & value1,vector<vector<string>> * options)892 void OpenVPNDriver::AppendOption(
893     const string& option,
894     const string& value0,
895     const string& value1,
896     vector<vector<string>>* options) {
897   options->push_back(vector<string>{ option, value0, value1 });
898 }
899 
AppendValueOption(const string & property,const string & option,vector<vector<string>> * options)900 bool OpenVPNDriver::AppendValueOption(
901     const string& property,
902     const string& option,
903     vector<vector<string>>* options) {
904   string value = args()->LookupString(property, "");
905   if (!value.empty()) {
906     AppendOption(option, value, options);
907     return true;
908   }
909   return false;
910 }
911 
AppendDelimitedValueOption(const string & property,const string & option,char delimiter,vector<vector<string>> * options)912 bool OpenVPNDriver::AppendDelimitedValueOption(
913     const string& property,
914     const string& option,
915     char delimiter,
916     vector<vector<string>>* options) {
917   string value = args()->LookupString(property, "");
918   if (!value.empty()) {
919     vector<string> parts = SplitString(
920         value, std::string{delimiter}, base::TRIM_WHITESPACE,
921         base::SPLIT_WANT_ALL);
922     parts.insert(parts.begin(), option);
923     options->push_back(parts);
924     return true;
925   }
926   return false;
927 }
928 
AppendFlag(const string & property,const string & option,vector<vector<string>> * options)929 bool OpenVPNDriver::AppendFlag(
930     const string& property,
931     const string& option,
932     vector<vector<string>>* options) {
933   if (args()->ContainsString(property)) {
934     AppendOption(option, options);
935     return true;
936   }
937   return false;
938 }
939 
GetServiceRpcIdentifier() const940 string OpenVPNDriver::GetServiceRpcIdentifier() const {
941   if (service_ == nullptr)
942     return "(openvpn_driver)";
943   return service_->GetRpcIdentifier();
944 }
945 
Disconnect()946 void OpenVPNDriver::Disconnect() {
947   SLOG(this, 2) << __func__;
948   IdleService();
949 }
950 
OnConnectionDisconnected()951 void OpenVPNDriver::OnConnectionDisconnected() {
952   LOG(INFO) << "Underlying connection disconnected.";
953   // Restart the OpenVPN client forcing a reconnect attempt.
954   management_server_->Restart();
955   // Indicate reconnect state right away to drop the VPN connection and start
956   // the connect timeout. This ensures that any miscommunication between shill
957   // and openvpn will not lead to a permanently stale connectivity state. Note
958   // that a subsequent invocation of OnReconnecting due to a RECONNECTING
959   // message will essentially be a no-op.
960   OnReconnecting(kReconnectReasonOffline);
961 }
962 
OnConnectTimeout()963 void OpenVPNDriver::OnConnectTimeout() {
964   VPNDriver::OnConnectTimeout();
965   Service::ConnectFailure failure =
966       management_server_->state() == OpenVPNManagementServer::kStateResolve ?
967       Service::kFailureDNSLookup : Service::kFailureConnect;
968   FailService(failure, Service::kErrorDetailsNone);
969 }
970 
OnReconnecting(ReconnectReason reason)971 void OpenVPNDriver::OnReconnecting(ReconnectReason reason) {
972   LOG(INFO) << __func__ << "(" << reason << ")";
973   int timeout_seconds = GetReconnectTimeoutSeconds(reason);
974   if (reason == kReconnectReasonTLSError &&
975       timeout_seconds < connect_timeout_seconds()) {
976     // Reconnect due to TLS error happens during connect so we need to cancel
977     // the original connect timeout first and then reduce the time limit.
978     StopConnectTimeout();
979   }
980   StartConnectTimeout(timeout_seconds);
981   // On restart/reconnect, drop the VPN connection, if any. The openvpn client
982   // might be in hold state if the VPN connection was previously established
983   // successfully. The hold will be released by OnDefaultServiceChanged when a
984   // new default service connects. This ensures that the client will use a fully
985   // functional underlying connection to reconnect.
986   if (device_) {
987     device_->DropConnection();
988   }
989   if (service_) {
990     service_->SetState(Service::kStateAssociating);
991   }
992 }
993 
994 // static
GetReconnectTimeoutSeconds(ReconnectReason reason)995 int OpenVPNDriver::GetReconnectTimeoutSeconds(ReconnectReason reason) {
996   switch (reason) {
997     case kReconnectReasonOffline:
998       return kReconnectOfflineTimeoutSeconds;
999     case kReconnectReasonTLSError:
1000       return kReconnectTLSErrorTimeoutSeconds;
1001     default:
1002       break;
1003   }
1004   return kDefaultConnectTimeoutSeconds;
1005 }
1006 
GetProviderType() const1007 string OpenVPNDriver::GetProviderType() const {
1008   return kProviderOpenVpn;
1009 }
1010 
GetProvider(Error * error)1011 KeyValueStore OpenVPNDriver::GetProvider(Error* error) {
1012   SLOG(this, 2) << __func__;
1013   KeyValueStore props = VPNDriver::GetProvider(error);
1014   props.SetBool(kPassphraseRequiredProperty,
1015                 args()->LookupString(kOpenVPNPasswordProperty, "").empty() &&
1016                 args()->LookupString(kOpenVPNTokenProperty, "").empty());
1017   return props;
1018 }
1019 
GetEnvironment()1020 map<string, string> OpenVPNDriver::GetEnvironment() {
1021   SLOG(this, 2) << __func__ << "(" << lsb_release_file_.value() << ")";
1022   map<string, string> environment;
1023   string contents;
1024   if (!base::ReadFileToString(lsb_release_file_, &contents)) {
1025     LOG(ERROR) << "Unable to read the lsb-release file: "
1026                << lsb_release_file_.value();
1027     return environment;
1028   }
1029   vector<string> lines = SplitString(contents, "\n", base::TRIM_WHITESPACE,
1030                                      base::SPLIT_WANT_ALL);
1031   for (const auto& line : lines) {
1032     const size_t assign = line.find('=');
1033     if (assign == string::npos) {
1034       continue;
1035     }
1036     const string key = line.substr(0, assign);
1037     const string value = line.substr(assign + 1);
1038     if (key == kChromeOSReleaseName) {
1039       environment[kOpenVPNEnvVarPlatformName] = value;
1040     } else if (key == kChromeOSReleaseVersion) {
1041       environment[kOpenVPNEnvVarPlatformVersion] = value;
1042     }
1043     // Other LSB release values are irrelevant.
1044   }
1045   return environment;
1046 }
1047 
OnDefaultServiceChanged(const ServiceRefPtr & service)1048 void OpenVPNDriver::OnDefaultServiceChanged(const ServiceRefPtr& service) {
1049   SLOG(this, 2) << __func__
1050                 << "(" << (service ? service->unique_name() : "-") << ")";
1051   // Allow the openvpn client to connect/reconnect only over a connected
1052   // underlying default service. If there's no default connected service, hold
1053   // the openvpn client until an underlying connection is established. If the
1054   // default service is our VPN service, hold the openvpn client on reconnect so
1055   // that the VPN connection can be torn down fully before a new connection
1056   // attempt is made over the underlying service.
1057   if (service && service != service_ && service->IsConnected()) {
1058     management_server_->ReleaseHold();
1059   } else {
1060     management_server_->Hold();
1061   }
1062 }
1063 
ReportConnectionMetrics()1064 void OpenVPNDriver::ReportConnectionMetrics() {
1065   metrics_->SendEnumToUMA(
1066       Metrics::kMetricVpnDriver,
1067       Metrics::kVpnDriverOpenVpn,
1068       Metrics::kMetricVpnDriverMax);
1069 
1070   if (args()->LookupString(kOpenVPNCaCertProperty, "") != "" ||
1071       (args()->ContainsStrings(kOpenVPNCaCertPemProperty) &&
1072        !args()->GetStrings(kOpenVPNCaCertPemProperty).empty())) {
1073     metrics_->SendEnumToUMA(
1074         Metrics::kMetricVpnRemoteAuthenticationType,
1075         Metrics::kVpnRemoteAuthenticationTypeOpenVpnCertificate,
1076         Metrics::kMetricVpnRemoteAuthenticationTypeMax);
1077   } else {
1078     metrics_->SendEnumToUMA(
1079         Metrics::kMetricVpnRemoteAuthenticationType,
1080         Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
1081         Metrics::kMetricVpnRemoteAuthenticationTypeMax);
1082   }
1083 
1084   bool has_user_authentication = false;
1085   if (args()->LookupString(kOpenVPNTokenProperty, "") != "") {
1086     metrics_->SendEnumToUMA(
1087         Metrics::kMetricVpnUserAuthenticationType,
1088         Metrics::kVpnUserAuthenticationTypeOpenVpnUsernameToken,
1089         Metrics::kMetricVpnUserAuthenticationTypeMax);
1090     has_user_authentication = true;
1091   }
1092   if (args()->LookupString(kOpenVPNOTPProperty, "") != "") {
1093     metrics_->SendEnumToUMA(
1094         Metrics::kMetricVpnUserAuthenticationType,
1095         Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp,
1096         Metrics::kMetricVpnUserAuthenticationTypeMax);
1097     has_user_authentication = true;
1098   }
1099   if (args()->LookupString(kOpenVPNAuthUserPassProperty, "") != "" ||
1100       args()->LookupString(kOpenVPNUserProperty, "") != "")  {
1101     metrics_->SendEnumToUMA(
1102         Metrics::kMetricVpnUserAuthenticationType,
1103         Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword,
1104         Metrics::kMetricVpnUserAuthenticationTypeMax);
1105     has_user_authentication = true;
1106   }
1107   if (args()->LookupString(kOpenVPNClientCertIdProperty, "") != "" ||
1108       args()->LookupString(kOpenVPNCertProperty, "") != "") {
1109     metrics_->SendEnumToUMA(
1110         Metrics::kMetricVpnUserAuthenticationType,
1111         Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate,
1112         Metrics::kMetricVpnUserAuthenticationTypeMax);
1113     has_user_authentication = true;
1114   }
1115   if (!has_user_authentication) {
1116     metrics_->SendEnumToUMA(
1117         Metrics::kMetricVpnUserAuthenticationType,
1118         Metrics::kVpnUserAuthenticationTypeOpenVpnNone,
1119         Metrics::kMetricVpnUserAuthenticationTypeMax);
1120   }
1121 }
1122 
1123 }  // namespace shill
1124