Home | History | Annotate | Download | only in vpn
      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;
     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 
     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 
    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 
    182 OpenVPNDriver::~OpenVPNDriver() {
    183   IdleService();
    184 }
    185 
    186 void OpenVPNDriver::IdleService() {
    187   Cleanup(Service::kStateIdle,
    188           Service::kFailureUnknown,
    189           Service::kErrorDetailsNone);
    190 }
    191 
    192 void OpenVPNDriver::FailService(Service::ConnectFailure failure,
    193                                 const string& error_details) {
    194   Cleanup(Service::kStateFailure, failure, error_details);
    195 }
    196 
    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
    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 
    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 
    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 
    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
    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 
    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 
    387 void OpenVPNDriver::GetLogin(string* /*user*/, string* /*password*/) {
    388   NOTREACHED();
    389 }
    390 
    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 
    406 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
    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
    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
    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
    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
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    880 void OpenVPNDriver::AppendOption(
    881     const string& option, vector<vector<string>>* options) {
    882   options->push_back(vector<string>{ option });
    883 }
    884 
    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 
    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 
    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 
    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 
    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 
    940 string OpenVPNDriver::GetServiceRpcIdentifier() const {
    941   if (service_ == nullptr)
    942     return "(openvpn_driver)";
    943   return service_->GetRpcIdentifier();
    944 }
    945 
    946 void OpenVPNDriver::Disconnect() {
    947   SLOG(this, 2) << __func__;
    948   IdleService();
    949 }
    950 
    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 
    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 
    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
    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 
   1007 string OpenVPNDriver::GetProviderType() const {
   1008   return kProviderOpenVpn;
   1009 }
   1010 
   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 
   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 
   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 
   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