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