• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // The term "L2TP / IPSec" refers to a pair of layered protocols used
18 // together to establish a tunneled VPN connection.  First, an "IPSec"
19 // link is created, which secures a single IP traffic pair between the
20 // client and server.  For this link to complete, one or two levels of
21 // authentication are performed.  The first, inner mandatory authentication
22 // ensures the two parties establishing the IPSec link are correct.  This
23 // can use a certificate exchange or a less secure "shared group key"
24 // (PSK) authentication.  An optional outer IPSec authentication can also be
25 // performed, which is not fully supported by shill's implementation.
26 // In order to support "tunnel groups" from some vendor VPNs shill supports
27 // supplying the authentication realm portion during the outer authentication.
28 // Notably, XAUTH and other forms of user authentication on this outer link
29 // are not supported.
30 //
31 // When IPSec authentication completes, traffic is tunneled through a
32 // layer 2 tunnel, called "L2TP".  Using the secured link, we tunnel a
33 // PPP link, through which a second layer of authentication is performed,
34 // using the provided "user" and "password" properties.
35 
36 #include "shill/vpn/l2tp_ipsec_driver.h"
37 
38 #include <base/bind.h>
39 #include <base/files/file_util.h>
40 #include <base/strings/string_util.h>
41 #include <base/strings/stringprintf.h>
42 #if defined(__ANDROID__)
43 #include <dbus/service_constants.h>
44 #else
45 #include <chromeos/dbus/service_constants.h>
46 #endif  // __ANDROID__
47 #include <vpn-manager/service_error.h>
48 
49 #include "shill/certificate_file.h"
50 #include "shill/device_info.h"
51 #include "shill/error.h"
52 #include "shill/external_task.h"
53 #include "shill/ipconfig.h"
54 #include "shill/logging.h"
55 #include "shill/manager.h"
56 #include "shill/ppp_daemon.h"
57 #include "shill/ppp_device.h"
58 #include "shill/ppp_device_factory.h"
59 #include "shill/process_manager.h"
60 #include "shill/vpn/vpn_service.h"
61 
62 using base::Bind;
63 using base::Closure;
64 using base::FilePath;
65 using std::map;
66 using std::string;
67 using std::vector;
68 
69 namespace shill {
70 
71 namespace Logging {
72 static auto kModuleLogScope = ScopeLogger::kVPN;
ObjectID(L2TPIPSecDriver * l)73 static string ObjectID(L2TPIPSecDriver* l) {
74   return l->GetServiceRpcIdentifier();
75 }
76 }
77 
78 namespace {
79 const char kL2TPIPSecIPSecTimeoutProperty[] = "L2TPIPsec.IPsecTimeout";
80 const char kL2TPIPSecLeftProtoPortProperty[] = "L2TPIPsec.LeftProtoPort";
81 const char kL2TPIPSecLengthBitProperty[] = "L2TPIPsec.LengthBit";
82 const char kL2TPIPSecPFSProperty[] = "L2TPIPsec.PFS";
83 const char kL2TPIPSecRefusePapProperty[] = "L2TPIPsec.RefusePap";
84 const char kL2TPIPSecRekeyProperty[] = "L2TPIPsec.Rekey";
85 const char kL2TPIPSecRequireAuthProperty[] = "L2TPIPsec.RequireAuth";
86 const char kL2TPIPSecRequireChapProperty[] = "L2TPIPsec.RequireChap";
87 const char kL2TPIPSecRightProtoPortProperty[] = "L2TPIPsec.RightProtoPort";
88 }  // namespace
89 
90 // static
91 const char L2TPIPSecDriver::kL2TPIPSecVPNPath[] = "/usr/sbin/l2tpipsec_vpn";
92 // static
93 const VPNDriver::Property L2TPIPSecDriver::kProperties[] = {
94   { kL2tpIpsecAuthenticationType, 0 },
95   { kL2tpIpsecCaCertNssProperty, 0 },
96   { kL2tpIpsecClientCertIdProperty, 0 },
97   { kL2tpIpsecClientCertSlotProperty, 0 },
98   { kL2tpIpsecIkeVersion, 0 },
99   { kL2tpIpsecPasswordProperty, Property::kCredential | Property::kWriteOnly },
100   { kL2tpIpsecPinProperty, Property::kCredential },
101   { kL2tpIpsecPskProperty, Property::kCredential | Property::kWriteOnly },
102   { kL2tpIpsecUserProperty, 0 },
103   { kProviderHostProperty, 0 },
104   { kProviderTypeProperty, 0 },
105   { kL2tpIpsecCaCertPemProperty, Property::kArray },
106   { kL2tpIpsecTunnelGroupProperty, 0 },
107   { kL2TPIPSecIPSecTimeoutProperty, 0 },
108   { kL2TPIPSecLeftProtoPortProperty, 0 },
109   { kL2TPIPSecLengthBitProperty, 0 },
110   { kL2TPIPSecPFSProperty, 0 },
111   { kL2TPIPSecRefusePapProperty, 0 },
112   { kL2TPIPSecRekeyProperty, 0 },
113   { kL2TPIPSecRequireAuthProperty, 0 },
114   { kL2TPIPSecRequireChapProperty, 0 },
115   { kL2TPIPSecRightProtoPortProperty, 0 },
116   { kL2tpIpsecXauthUserProperty, Property::kCredential | Property::kWriteOnly },
117   { kL2tpIpsecXauthPasswordProperty,
118     Property::kCredential | Property::kWriteOnly },
119   { kL2tpIpsecLcpEchoDisabledProperty, 0 },
120 };
121 
L2TPIPSecDriver(ControlInterface * control,EventDispatcher * dispatcher,Metrics * metrics,Manager * manager,DeviceInfo * device_info,ProcessManager * process_manager)122 L2TPIPSecDriver::L2TPIPSecDriver(ControlInterface* control,
123                                  EventDispatcher* dispatcher,
124                                  Metrics* metrics,
125                                  Manager* manager,
126                                  DeviceInfo* device_info,
127                                  ProcessManager* process_manager)
128     : VPNDriver(dispatcher, manager, kProperties, arraysize(kProperties)),
129       control_(control),
130       metrics_(metrics),
131       device_info_(device_info),
132       process_manager_(process_manager),
133       ppp_device_factory_(PPPDeviceFactory::GetInstance()),
134       certificate_file_(new CertificateFile()),
135       weak_ptr_factory_(this) {}
136 
~L2TPIPSecDriver()137 L2TPIPSecDriver::~L2TPIPSecDriver() {
138   IdleService();
139 }
140 
GetServiceRpcIdentifier()141 std::string L2TPIPSecDriver::GetServiceRpcIdentifier() {
142   if (service_ == nullptr)
143     return "(l2tp_ipsec_driver)";
144   return service_->GetRpcIdentifier();
145 }
146 
ClaimInterface(const string & link_name,int interface_index)147 bool L2TPIPSecDriver::ClaimInterface(const string& link_name,
148                                      int interface_index) {
149   // TODO(petkov): crbug.com/212446.
150   NOTIMPLEMENTED();
151   return false;
152 }
153 
Connect(const VPNServiceRefPtr & service,Error * error)154 void L2TPIPSecDriver::Connect(const VPNServiceRefPtr& service, Error* error) {
155   StartConnectTimeout(kDefaultConnectTimeoutSeconds);
156   service_ = service;
157   service_->SetState(Service::kStateConfiguring);
158   if (!SpawnL2TPIPSecVPN(error)) {
159     FailService(Service::kFailureInternal);
160   }
161 }
162 
Disconnect()163 void L2TPIPSecDriver::Disconnect() {
164   SLOG(this, 2) << __func__;
165   IdleService();
166 }
167 
OnConnectionDisconnected()168 void L2TPIPSecDriver::OnConnectionDisconnected() {
169   LOG(INFO) << "Underlying connection disconnected.";
170   IdleService();
171 }
172 
OnConnectTimeout()173 void L2TPIPSecDriver::OnConnectTimeout() {
174   VPNDriver::OnConnectTimeout();
175   FailService(Service::kFailureConnect);
176 }
177 
GetProviderType() const178 string L2TPIPSecDriver::GetProviderType() const {
179   return kProviderL2tpIpsec;
180 }
181 
IdleService()182 void L2TPIPSecDriver::IdleService() {
183   Cleanup(Service::kStateIdle, Service::kFailureUnknown);
184 }
185 
FailService(Service::ConnectFailure failure)186 void L2TPIPSecDriver::FailService(Service::ConnectFailure failure) {
187   Cleanup(Service::kStateFailure, failure);
188 }
189 
Cleanup(Service::ConnectState state,Service::ConnectFailure failure)190 void L2TPIPSecDriver::Cleanup(Service::ConnectState state,
191                               Service::ConnectFailure failure) {
192   SLOG(this, 2) << __func__ << "("
193                 << Service::ConnectStateToString(state) << ", "
194                 << Service::ConnectFailureToString(failure) << ")";
195   StopConnectTimeout();
196   DeleteTemporaryFiles();
197   external_task_.reset();
198   if (device_) {
199     device_->DropConnection();
200     device_->SetEnabled(false);
201     device_ = nullptr;
202   }
203   if (service_) {
204     if (state == Service::kStateFailure) {
205       service_->SetFailure(failure);
206     } else {
207       service_->SetState(state);
208     }
209     service_ = nullptr;
210   }
211 }
212 
DeleteTemporaryFile(base::FilePath * temporary_file)213 void L2TPIPSecDriver::DeleteTemporaryFile(base::FilePath* temporary_file) {
214   if (!temporary_file->empty()) {
215     base::DeleteFile(*temporary_file, false);
216     temporary_file->clear();
217   }
218 }
219 
DeleteTemporaryFiles()220 void L2TPIPSecDriver::DeleteTemporaryFiles() {
221   DeleteTemporaryFile(&psk_file_);
222   DeleteTemporaryFile(&xauth_credentials_file_);
223 }
224 
SpawnL2TPIPSecVPN(Error * error)225 bool L2TPIPSecDriver::SpawnL2TPIPSecVPN(Error* error) {
226   SLOG(this, 2) << __func__;
227   std::unique_ptr<ExternalTask> external_task_local(
228       new ExternalTask(control_,
229                        process_manager_,
230                        weak_ptr_factory_.GetWeakPtr(),
231                        Bind(&L2TPIPSecDriver::OnL2TPIPSecVPNDied,
232                             weak_ptr_factory_.GetWeakPtr())));
233 
234   vector<string> options;
235   map<string, string> environment;  // No env vars passed.
236   if (!InitOptions(&options, error)) {
237     return false;
238   }
239   LOG(INFO) << "L2TP/IPSec VPN process options: "
240             << base::JoinString(options, " ");
241 
242   if (external_task_local->Start(
243           FilePath(kL2TPIPSecVPNPath), options, environment, true, error)) {
244     external_task_ = std::move(external_task_local);
245     return true;
246   }
247   return false;
248 }
249 
InitOptions(vector<string> * options,Error * error)250 bool L2TPIPSecDriver::InitOptions(vector<string>* options, Error* error) {
251   string vpnhost = args()->LookupString(kProviderHostProperty, "");
252   if (vpnhost.empty()) {
253     Error::PopulateAndLog(
254         FROM_HERE, error, Error::kInvalidArguments, "VPN host not specified.");
255     return false;
256   }
257 
258   if (!InitPSKOptions(options, error)) {
259     return false;
260   }
261 
262   if (!InitXauthOptions(options, error)) {
263     return false;
264   }
265 
266   options->push_back(base::StringPrintf("--remote_host=%s", vpnhost.c_str()));
267   options->push_back(base::StringPrintf("--pppd_plugin=%s",
268                                         PPPDaemon::kShimPluginPath));
269   // Disable pppd from configuring IP addresses, routes, DNS.
270   options->push_back("--nosystemconfig");
271 
272   // Accept a PEM CA certificate.
273   InitPEMOptions(options);
274 
275   AppendValueOption(kL2tpIpsecClientCertIdProperty,
276                     "--client_cert_id", options);
277   AppendValueOption(kL2tpIpsecClientCertSlotProperty,
278                     "--client_cert_slot", options);
279   AppendValueOption(kL2tpIpsecPinProperty, "--user_pin", options);
280   AppendValueOption(kL2tpIpsecUserProperty, "--user", options);
281   AppendValueOption(kL2TPIPSecIPSecTimeoutProperty, "--ipsec_timeout", options);
282   AppendValueOption(kL2TPIPSecLeftProtoPortProperty,
283                     "--leftprotoport", options);
284   AppendFlag(kL2TPIPSecPFSProperty, "--pfs", "--nopfs", options);
285   AppendFlag(kL2TPIPSecRekeyProperty, "--rekey", "--norekey", options);
286   AppendValueOption(kL2TPIPSecRightProtoPortProperty,
287                     "--rightprotoport", options);
288   AppendFlag(kL2TPIPSecRequireChapProperty,
289              "--require_chap", "--norequire_chap", options);
290   AppendFlag(kL2TPIPSecRefusePapProperty,
291              "--refuse_pap", "--norefuse_pap", options);
292   AppendFlag(kL2TPIPSecRequireAuthProperty,
293              "--require_authentication", "--norequire_authentication", options);
294   AppendFlag(kL2TPIPSecLengthBitProperty,
295              "--length_bit", "--nolength_bit", options);
296   AppendFlag(kL2tpIpsecLcpEchoDisabledProperty,
297              "--noppp_lcp_echo", "--ppp_lcp_echo", options);
298   AppendValueOption(kL2tpIpsecTunnelGroupProperty, "--tunnel_group", options);
299   if (SLOG_IS_ON(VPN, 0)) {
300     options->push_back("--debug");
301   }
302   return true;
303 }
304 
InitPSKOptions(vector<string> * options,Error * error)305 bool L2TPIPSecDriver::InitPSKOptions(vector<string>* options, Error* error) {
306   string psk = args()->LookupString(kL2tpIpsecPskProperty, "");
307   if (!psk.empty()) {
308     if (!base::CreateTemporaryFileInDir(manager()->run_path(), &psk_file_) ||
309         chmod(psk_file_.value().c_str(), S_IRUSR | S_IWUSR) ||
310         base::WriteFile(psk_file_, psk.data(), psk.size()) !=
311             static_cast<int>(psk.size())) {
312       Error::PopulateAndLog(
313           FROM_HERE, error, Error::kInternalError, "Unable to setup psk file.");
314       return false;
315     }
316     options->push_back(base::StringPrintf("--psk_file=%s",
317                                           psk_file_.value().c_str()));
318   }
319   return true;
320 }
321 
InitPEMOptions(vector<string> * options)322 bool L2TPIPSecDriver::InitPEMOptions(vector<string>* options) {
323   vector<string> ca_certs;
324   if (args()->ContainsStrings(kL2tpIpsecCaCertPemProperty)) {
325     ca_certs = args()->GetStrings(kL2tpIpsecCaCertPemProperty);
326   }
327   if (ca_certs.empty()) {
328     return false;
329   }
330   FilePath certfile = certificate_file_->CreatePEMFromStrings(ca_certs);
331   if (certfile.empty()) {
332     LOG(ERROR) << "Unable to extract certificates from PEM string.";
333     return false;
334   }
335   options->push_back(base::StringPrintf("--server_ca_file=%s",
336                                         certfile.value().c_str()));
337   return true;
338 }
339 
InitXauthOptions(vector<string> * options,Error * error)340 bool L2TPIPSecDriver::InitXauthOptions(vector<string>* options, Error* error) {
341   string user = args()->LookupString(kL2tpIpsecXauthUserProperty, "");
342   string password = args()->LookupString(kL2tpIpsecXauthPasswordProperty, "");
343   if (user.empty() && password.empty()) {
344     // Xauth credentials not configured.
345     return true;
346   }
347   if (user.empty() || password.empty()) {
348       Error::PopulateAndLog(
349           FROM_HERE, error, Error::kInvalidArguments,
350           "XAUTH credentials are partially configured.");
351     return false;
352   }
353   string xauth_credentials = user + "\n" + password + "\n";
354   if (!base::CreateTemporaryFileInDir(manager()->run_path(),
355                                       &xauth_credentials_file_) ||
356       chmod(xauth_credentials_file_.value().c_str(), S_IRUSR | S_IWUSR) ||
357       base::WriteFile(xauth_credentials_file_, xauth_credentials.data(),
358                       xauth_credentials.size()) !=
359           static_cast<int>(xauth_credentials.size())) {
360     Error::PopulateAndLog(
361         FROM_HERE, error, Error::kInternalError,
362         "Unable to setup XAUTH credentials file.");
363     return false;
364   }
365   options->push_back(base::StringPrintf("--xauth_credentials_file=%s",
366                          xauth_credentials_file_.value().c_str()));
367   return true;
368 }
369 
AppendValueOption(const string & property,const string & option,vector<string> * options)370 bool L2TPIPSecDriver::AppendValueOption(
371     const string& property, const string& option, vector<string>* options) {
372   string value = args()->LookupString(property, "");
373   if (!value.empty()) {
374     options->push_back(base::StringPrintf("%s=%s", option.c_str(),
375                                           value.c_str()));
376     return true;
377   }
378   return false;
379 }
380 
AppendFlag(const string & property,const string & true_option,const string & false_option,vector<string> * options)381 bool L2TPIPSecDriver::AppendFlag(const string& property,
382                                  const string& true_option,
383                                  const string& false_option,
384                                  vector<string>* options) {
385   string value = args()->LookupString(property, "");
386   if (!value.empty()) {
387     options->push_back(value == "true" ? true_option : false_option);
388     return true;
389   }
390   return false;
391 }
392 
OnL2TPIPSecVPNDied(pid_t,int status)393 void L2TPIPSecDriver::OnL2TPIPSecVPNDied(pid_t /*pid*/, int status) {
394   FailService(TranslateExitStatusToFailure(status));
395   // TODO(petkov): Figure if we need to restart the connection.
396 }
397 
398 // static
TranslateExitStatusToFailure(int status)399 Service::ConnectFailure L2TPIPSecDriver::TranslateExitStatusToFailure(
400     int status) {
401   if (!WIFEXITED(status)) {
402     return Service::kFailureInternal;
403   }
404   switch (WEXITSTATUS(status)) {
405     case vpn_manager::kServiceErrorResolveHostnameFailed:
406       return Service::kFailureDNSLookup;
407     case vpn_manager::kServiceErrorIpsecConnectionFailed:
408     case vpn_manager::kServiceErrorL2tpConnectionFailed:
409     case vpn_manager::kServiceErrorPppConnectionFailed:
410       return Service::kFailureConnect;
411     case vpn_manager::kServiceErrorIpsecPresharedKeyAuthenticationFailed:
412       return Service::kFailureIPSecPSKAuth;
413     case vpn_manager::kServiceErrorIpsecCertificateAuthenticationFailed:
414       return Service::kFailureIPSecCertAuth;
415     case vpn_manager::kServiceErrorPppAuthenticationFailed:
416       return Service::kFailurePPPAuth;
417     default:
418       break;
419   }
420   return Service::kFailureUnknown;
421 }
422 
GetLogin(string * user,string * password)423 void L2TPIPSecDriver::GetLogin(string* user, string* password) {
424   LOG(INFO) << "Login requested.";
425   string user_property =
426       args()->LookupString(kL2tpIpsecUserProperty, "");
427   if (user_property.empty()) {
428     LOG(ERROR) << "User not set.";
429     return;
430   }
431   string password_property =
432       args()->LookupString(kL2tpIpsecPasswordProperty, "");
433   if (password_property.empty()) {
434     LOG(ERROR) << "Password not set.";
435     return;
436   }
437   *user = user_property;
438   *password = password_property;
439 }
440 
Notify(const string & reason,const map<string,string> & dict)441 void L2TPIPSecDriver::Notify(
442     const string& reason, const map<string, string>& dict) {
443   LOG(INFO) << "IP configuration received: " << reason;
444 
445   if (reason == kPPPReasonAuthenticating ||
446       reason == kPPPReasonAuthenticated) {
447     // These are uninteresting intermediate states that do not indicate failure.
448     return;
449   }
450 
451   if (reason != kPPPReasonConnect) {
452     DCHECK_EQ(kPPPReasonDisconnect, reason);
453     // DestroyLater, rather than while on stack.
454     external_task_.release()->DestroyLater(dispatcher());
455     FailService(Service::kFailureUnknown);
456     return;
457   }
458 
459   DeleteTemporaryFiles();
460 
461   string interface_name = PPPDevice::GetInterfaceName(dict);
462   int interface_index = device_info_->GetIndex(interface_name);
463   if (interface_index < 0) {
464     // TODO(petkov): Consider handling the race when the RTNL notification about
465     // the new PPP device has not been received yet. We can keep the IP
466     // configuration and apply it when ClaimInterface is
467     // invoked. crbug.com/212446.
468     NOTIMPLEMENTED() << ": No device info for " << interface_name << ".";
469     return;
470   }
471 
472   // There is no IPv6 support for L2TP/IPsec VPN at this moment, so create a
473   // blackhole route for IPv6 traffic after establishing a IPv4 VPN.
474   // TODO(benchan): Generalize this when IPv6 support is added.
475   bool blackhole_ipv6 = true;
476 
477   if (!device_) {
478     device_ = ppp_device_factory_->CreatePPPDevice(
479         control_, dispatcher(), metrics_, manager(), interface_name,
480         interface_index);
481   }
482   device_->SetEnabled(true);
483   device_->SelectService(service_);
484 
485   // Reduce MTU to the minimum viable for IPv6, since the IPSec layer consumes
486   // some variable portion of the payload.  Although this system does not yet
487   // support IPv6, it is a reasonable value to start with, since the minimum
488   // IPv6 packet size will plausibly be a size any gateway would support, and
489   // is also larger than the IPv4 minimum size.
490   device_->UpdateIPConfigFromPPPWithMTU(
491       dict, blackhole_ipv6, IPConfig::kMinIPv6MTU);
492 
493   ReportConnectionMetrics();
494   StopConnectTimeout();
495 }
496 
IsPskRequired() const497 bool L2TPIPSecDriver::IsPskRequired() const {
498   return
499     const_args()->LookupString(kL2tpIpsecPskProperty, "").empty() &&
500     const_args()->LookupString(kL2tpIpsecClientCertIdProperty, "").empty();
501 }
502 
GetProvider(Error * error)503 KeyValueStore L2TPIPSecDriver::GetProvider(Error* error) {
504   SLOG(this, 2) << __func__;
505   KeyValueStore props = VPNDriver::GetProvider(error);
506   props.SetBool(kPassphraseRequiredProperty,
507                 args()->LookupString(kL2tpIpsecPasswordProperty, "").empty());
508   props.SetBool(kL2tpIpsecPskRequiredProperty, IsPskRequired());
509   return props;
510 }
511 
ReportConnectionMetrics()512 void L2TPIPSecDriver::ReportConnectionMetrics() {
513   metrics_->SendEnumToUMA(
514       Metrics::kMetricVpnDriver,
515       Metrics::kVpnDriverL2tpIpsec,
516       Metrics::kMetricVpnDriverMax);
517 
518   // We output an enum for each of the authentication types specified,
519   // even if more than one is set at the same time.
520   bool has_remote_authentication = false;
521   if (args()->LookupString(kL2tpIpsecPskProperty, "") != "") {
522     metrics_->SendEnumToUMA(
523         Metrics::kMetricVpnRemoteAuthenticationType,
524         Metrics::kVpnRemoteAuthenticationTypeL2tpIpsecPsk,
525         Metrics::kMetricVpnRemoteAuthenticationTypeMax);
526     has_remote_authentication = true;
527   }
528   if (!has_remote_authentication) {
529     metrics_->SendEnumToUMA(
530         Metrics::kMetricVpnRemoteAuthenticationType,
531         Metrics::kVpnRemoteAuthenticationTypeL2tpIpsecDefault,
532         Metrics::kMetricVpnRemoteAuthenticationTypeMax);
533   }
534 
535   bool has_user_authentication = false;
536   if (args()->LookupString(kL2tpIpsecClientCertIdProperty,
537                            "") != "") {
538     metrics_->SendEnumToUMA(
539         Metrics::kMetricVpnUserAuthenticationType,
540         Metrics::kVpnUserAuthenticationTypeL2tpIpsecCertificate,
541         Metrics::kMetricVpnUserAuthenticationTypeMax);
542     has_user_authentication = true;
543   }
544   if (args()->LookupString(kL2tpIpsecPasswordProperty, "") != "") {
545     metrics_->SendEnumToUMA(
546         Metrics::kMetricVpnUserAuthenticationType,
547         Metrics::kVpnUserAuthenticationTypeL2tpIpsecUsernamePassword,
548         Metrics::kMetricVpnUserAuthenticationTypeMax);
549     has_user_authentication = true;
550   }
551   if (!has_user_authentication) {
552     metrics_->SendEnumToUMA(
553         Metrics::kMetricVpnUserAuthenticationType,
554         Metrics::kVpnUserAuthenticationTypeL2tpIpsecNone,
555         Metrics::kMetricVpnUserAuthenticationTypeMax);
556   }
557 }
558 
559 }  // namespace shill
560