blob: 506ec60730526f29f8a3bc3ab0c24e0dc55c0045 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The term "L2TP/IPsec" refers to a pair of layered protocols used
// together to establish a tunneled VPN connection. First, an "IPsec"
// link is created, which secures a single IP traffic pair between the
// client and server. For this link to complete, one or two levels of
// authentication are performed. The first, inner mandatory authentication
// ensures the two parties establishing the IPsec link are correct. This
// can use a certificate exchange or a less secure "shared group key"
// (PSK) authentication. An optional outer IPsec authentication can also be
// performed, which is not fully supported by shill's implementation.
// In order to support "tunnel groups" from some vendor VPNs shill supports
// supplying the authentication realm portion during the outer authentication.
//
// When IPsec authentication completes, traffic is tunneled through a
// layer 2 tunnel, called "L2TP". Using the secured link, we tunnel a
// PPP link, through which a second layer of authentication is performed,
// using the provided "user" and "password" properties.
#include "shill/vpn/l2tp_ipsec_driver.h"
#include <iterator>
#include <memory>
#include <utility>
#include <base/bind.h>
#include <base/check.h>
#include <base/check_op.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <chromeos/dbus/service_constants.h>
#include <re2/re2.h>
#include <vpn-manager/service_error.h>
#include "shill/certificate_file.h"
#include "shill/device_info.h"
#include "shill/error.h"
#include "shill/external_task.h"
#include "shill/ipconfig.h"
#include "shill/logging.h"
#include "shill/manager.h"
#include "shill/ppp_daemon.h"
#include "shill/ppp_device.h"
#include "shill/process_manager.h"
#include "shill/scope_logger.h"
#include "shill/vpn/ipsec_connection.h"
#include "shill/vpn/vpn_service.h"
namespace shill {
namespace Logging {
static auto kModuleLogScope = ScopeLogger::kVPN;
static std::string ObjectID(const L2TPIPsecDriver*) {
return "(l2tp_ipsec_driver)";
}
} // namespace Logging
namespace {
const char kL2TPIPsecIPsecTimeoutProperty[] = "L2TPIPsec.IPsecTimeout";
const char kL2TPIPsecLeftProtoPortProperty[] = "L2TPIPsec.LeftProtoPort";
const char kL2TPIPsecLengthBitProperty[] = "L2TPIPsec.LengthBit";
const char kL2TPIPsecRefusePapProperty[] = "L2TPIPsec.RefusePap";
const char kL2TPIPsecRekeyProperty[] = "L2TPIPsec.Rekey";
const char kL2TPIPsecRequireAuthProperty[] = "L2TPIPsec.RequireAuth";
const char kL2TPIPsecRequireChapProperty[] = "L2TPIPsec.RequireChap";
const char kL2TPIPsecRightProtoPortProperty[] = "L2TPIPsec.RightProtoPort";
constexpr base::TimeDelta kConnectTimeout = base::Minutes(1);
constexpr char kStrokePath[] = "/usr/libexec/ipsec/stroke";
Service::ConnectFailure ExitStatusToFailure(int status) {
switch (status) {
case vpn_manager::kServiceErrorNoError:
return Service::kFailureNone;
case vpn_manager::kServiceErrorInternal:
case vpn_manager::kServiceErrorInvalidArgument:
return Service::kFailureInternal;
case vpn_manager::kServiceErrorResolveHostnameFailed:
return Service::kFailureDNSLookup;
case vpn_manager::kServiceErrorIpsecConnectionFailed:
case vpn_manager::kServiceErrorL2tpConnectionFailed:
case vpn_manager::kServiceErrorPppConnectionFailed:
return Service::kFailureConnect;
case vpn_manager::kServiceErrorIpsecPresharedKeyAuthenticationFailed:
return Service::kFailureIPsecPSKAuth;
case vpn_manager::kServiceErrorIpsecCertificateAuthenticationFailed:
return Service::kFailureIPsecCertAuth;
case vpn_manager::kServiceErrorPppAuthenticationFailed:
return Service::kFailurePPPAuth;
default:
return Service::kFailureUnknown;
}
}
void ReportConnectionEndReason(Metrics* metrics,
Service::ConnectFailure failure) {
metrics->SendEnumToUMA(Metrics::kMetricVpnL2tpIpsecStrokeEndReason,
Metrics::ConnectFailureToServiceErrorEnum(failure),
Metrics::kMetricVpnL2tpIpsecStrokeEndReasonMax);
}
} // namespace
// static
const char L2TPIPsecDriver::kL2TPIPsecVPNPath[] = "/usr/sbin/l2tpipsec_vpn";
// static
const VPNDriver::Property L2TPIPsecDriver::kProperties[] = {
{kL2TPIPsecClientCertIdProperty, 0},
{kL2TPIPsecClientCertSlotProperty, 0},
{kL2TPIPsecPasswordProperty, Property::kCredential | Property::kWriteOnly},
{kL2TPIPsecPinProperty, Property::kCredential},
{kL2TPIPsecPskProperty, Property::kCredential | Property::kWriteOnly},
{kL2TPIPsecUseLoginPasswordProperty, 0},
{kL2TPIPsecUserProperty, 0},
{kProviderHostProperty, 0},
{kProviderTypeProperty, 0},
{kL2TPIPsecCaCertPemProperty, Property::kArray},
{kL2TPIPsecTunnelGroupProperty, 0},
{kL2TPIPsecIPsecTimeoutProperty, 0},
{kL2TPIPsecLeftProtoPortProperty, 0},
{kL2TPIPsecLengthBitProperty, 0},
{kL2TPIPsecRefusePapProperty, 0},
{kL2TPIPsecRekeyProperty, 0},
{kL2TPIPsecRequireAuthProperty, 0},
{kL2TPIPsecRequireChapProperty, 0},
{kL2TPIPsecRightProtoPortProperty, 0},
{kL2TPIPsecXauthUserProperty, Property::kCredential | Property::kWriteOnly},
{kL2TPIPsecXauthPasswordProperty,
Property::kCredential | Property::kWriteOnly},
{kL2TPIPsecLcpEchoDisabledProperty, 0},
};
// static
bool L2TPIPsecDriver::ParseStrokeStatusAllOutput(
const std::string& stroke_output,
IPsecConnection::CipherSuite* ike_cipher,
IPsecConnection::CipherSuite* esp_cipher) {
CHECK(ike_cipher);
CHECK(esp_cipher);
// Does some basic check at first to make sure the SA is established. If the
// check failed, we cannot any reasonable results so we don't need to report
// an unknown to UMA.
constexpr char kSAHeaderLine[] =
"Security Associations (1 up, 0 connecting):";
if (stroke_output.find(kSAHeaderLine) == std::string::npos) {
LOG(ERROR) << "The output of stroke does not contain the SA header line, "
"output is: "
<< stroke_output;
return false;
}
// Will log |stdout_str| if any part of the parsing fails.
bool success = true;
// The IKE part has a prompt before it and a space after it. See
// l2tp_ipsec_driver_test.cc for the example. See `stroke_list.c:log_ike_sa()`
// for how this part is output.
std::string ike_matched_part;
static constexpr LazyRE2 kIKECipherSuite = {
R"(IKE proposal: ((?:[^/\s]+)(?:/[^/\s]+)*)\s+)"};
if (!RE2::PartialMatch(stroke_output, *kIKECipherSuite, &ike_matched_part)) {
LOG(ERROR) << "Failed to parse the IKE cipher suite";
success = false;
}
*ike_cipher = IPsecConnection::ParseCipherSuite(ike_matched_part);
// Matches the ESP part. There might be several child SAs at the same time.
// For each child SA, the cipher suite for ESP will be output at the second
// line, so we find the first line (the currently installed SA) at first and
// then match the cipher part on the second line. Note that "managed" is the
// name of the connection which is hard-coded in vpn-manager. See
// l2tp_ipsec_driver_test.cc for the example. See
// `stroke_list.c:log_child_sa()` for how this part is output.
std::string esp_matched_part;
static constexpr LazyRE2 kESPCipherSuite = {
R"(managed{\d+}: INSTALLED, TRANSPORT,[^\n]*\n +managed{\d+}: ((?:[^/\s,]+)(?:/[^/\s,]+)*),)"};
if (!RE2::PartialMatch(stroke_output, *kESPCipherSuite, &esp_matched_part)) {
LOG(ERROR) << "Failed to parse the ESP cipher suite";
success = false;
}
*esp_cipher = IPsecConnection::ParseCipherSuite(esp_matched_part);
if (!success) {
LOG(ERROR) << "The output of stroke is: " << stroke_output;
}
return true;
}
L2TPIPsecDriver::L2TPIPsecDriver(Manager* manager,
ProcessManager* process_manager)
: VPNDriver(manager, process_manager, kProperties, std::size(kProperties)),
certificate_file_(new CertificateFile()),
password_provider_(
std::make_unique<password_provider::PasswordProvider>()),
vpn_util_(VPNUtil::New()) {}
L2TPIPsecDriver::~L2TPIPsecDriver() {
Cleanup();
}
base::TimeDelta L2TPIPsecDriver::ConnectAsync(EventHandler* handler) {
event_handler_ = handler;
Error error;
if (!SpawnL2TPIPsecVPN(&error)) {
dispatcher()->PostTask(
FROM_HERE,
base::BindOnce(&L2TPIPsecDriver::FailService,
weak_factory_.GetWeakPtr(), Service::kFailureInternal));
return kTimeoutNone;
}
return kConnectTimeout;
}
void L2TPIPsecDriver::Disconnect() {
SLOG(this, 2) << __func__;
ReportConnectionEndReason(metrics(), Service::kFailureDisconnect);
Cleanup();
event_handler_ = nullptr;
}
IPConfig::Properties L2TPIPsecDriver::GetIPProperties() const {
return ip_properties_;
}
void L2TPIPsecDriver::OnConnectTimeout() {
FailService(Service::kFailureConnect);
}
std::string L2TPIPsecDriver::GetProviderType() const {
return kProviderL2tpIpsec;
}
void L2TPIPsecDriver::FailService(Service::ConnectFailure failure) {
SLOG(this, 2) << __func__ << "(" << Service::ConnectFailureToString(failure)
<< ")";
Cleanup();
if (event_handler_) {
// Only reports metrics when |event_handler_| exists to ensure reporting
// only once for each connection.
ReportConnectionEndReason(metrics(), failure);
event_handler_->OnDriverFailure(failure, Service::kErrorDetailsNone);
event_handler_ = nullptr;
}
}
void L2TPIPsecDriver::Cleanup() {
DeleteTemporaryFiles();
external_task_.reset();
}
void L2TPIPsecDriver::OnBeforeSuspend(const ResultCallback& callback) {
if (event_handler_) {
FailService(Service::kFailureDisconnect);
}
callback.Run(Error(Error::kSuccess));
}
void L2TPIPsecDriver::OnDefaultPhysicalServiceEvent(
DefaultPhysicalServiceEvent event) {
if (!event_handler_) {
return;
}
if (event == kDefaultPhysicalServiceUp) {
return;
}
FailService(Service::kFailureDisconnect);
}
void L2TPIPsecDriver::DeleteTemporaryFile(base::FilePath* temporary_file) {
if (!temporary_file->empty()) {
base::DeleteFile(*temporary_file);
temporary_file->clear();
}
}
void L2TPIPsecDriver::DeleteTemporaryFiles() {
DeleteTemporaryFile(&psk_file_);
DeleteTemporaryFile(&xauth_credentials_file_);
}
bool L2TPIPsecDriver::SpawnL2TPIPsecVPN(Error* error) {
SLOG(this, 2) << __func__;
auto external_task_local = std::make_unique<ExternalTask>(
control_interface(), process_manager(), weak_factory_.GetWeakPtr(),
base::Bind(&L2TPIPsecDriver::OnL2TPIPsecVPNDied,
weak_factory_.GetWeakPtr()));
std::vector<std::string> options;
const std::map<std::string, std::string> environment; // No env vars passed.
if (!InitOptions(&options, error)) {
return false;
}
LOG(INFO) << "L2TP/IPsec VPN process options: "
<< base::JoinString(options, " ");
constexpr uint64_t kCapMask = CAP_TO_MASK(CAP_NET_ADMIN) |
CAP_TO_MASK(CAP_NET_RAW) |
CAP_TO_MASK(CAP_NET_BIND_SERVICE);
if (!external_task_local->StartInMinijail(
base::FilePath(kL2TPIPsecVPNPath), &options, environment,
VPNUtil::BuildMinijailOptions(kCapMask), error)) {
return false;
}
external_task_ = std::move(external_task_local);
return true;
}
bool L2TPIPsecDriver::InitOptions(std::vector<std::string>* options,
Error* error) {
const auto vpnhost = args()->Lookup<std::string>(kProviderHostProperty, "");
if (vpnhost.empty()) {
Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
"VPN host not specified.");
return false;
}
if (!InitPSKOptions(options, error)) {
return false;
}
if (!InitXauthOptions(options, error)) {
return false;
}
options->push_back(base::StringPrintf("--remote_host=%s", vpnhost.c_str()));
options->push_back(
base::StringPrintf("--pppd_plugin=%s", PPPDaemon::kShimPluginPath));
// Disable pppd from configuring IP addresses, routes, DNS.
options->push_back("--nosystemconfig");
// Accept a PEM CA certificate.
InitPEMOptions(options);
AppendValueOption(kL2TPIPsecClientCertIdProperty, "--client_cert_id",
options);
AppendValueOption(kL2TPIPsecClientCertSlotProperty, "--client_cert_slot",
options);
AppendValueOption(kL2TPIPsecPinProperty, "--user_pin", options);
AppendValueOption(kL2TPIPsecUserProperty, "--user", options);
AppendValueOption(kL2TPIPsecIPsecTimeoutProperty, "--ipsec_timeout", options);
AppendValueOption(kL2TPIPsecLeftProtoPortProperty, "--leftprotoport",
options);
AppendFlag(kL2TPIPsecRekeyProperty, "--rekey", "--norekey", options);
AppendValueOption(kL2TPIPsecRightProtoPortProperty, "--rightprotoport",
options);
AppendFlag(kL2TPIPsecRequireChapProperty, "--require_chap",
"--norequire_chap", options);
// b/187984628: When UseLoginPassword is enabled, PAP must be refused to
// prevent potential password leak to a malicious server.
if (args()->Lookup<std::string>(kL2TPIPsecUseLoginPasswordProperty, "") ==
"true") {
args()->Set<std::string>(kL2TPIPsecRefusePapProperty, "true");
}
AppendFlag(kL2TPIPsecRefusePapProperty, "--refuse_pap", "--norefuse_pap",
options);
AppendFlag(kL2TPIPsecRequireAuthProperty, "--require_authentication",
"--norequire_authentication", options);
AppendFlag(kL2TPIPsecLengthBitProperty, "--length_bit", "--nolength_bit",
options);
AppendFlag(kL2TPIPsecLcpEchoDisabledProperty, "--noppp_lcp_echo",
"--ppp_lcp_echo", options);
AppendValueOption(kL2TPIPsecTunnelGroupProperty, "--tunnel_group", options);
if (SLOG_IS_ON(VPN, 0)) {
options->push_back(base::StringPrintf(
"--log_level=%d", -ScopeLogger::GetInstance()->verbose_level()));
}
return true;
}
bool L2TPIPsecDriver::InitPSKOptions(std::vector<std::string>* options,
Error* error) {
const auto psk = args()->Lookup<std::string>(kL2TPIPsecPskProperty, "");
if (!psk.empty()) {
if (!base::CreateTemporaryFileInDir(manager()->run_path(), &psk_file_) ||
!vpn_util_->WriteConfigFile(psk_file_, psk)) {
Error::PopulateAndLog(FROM_HERE, error, Error::kInternalError,
"Unable to setup psk file.");
return false;
}
options->push_back(
base::StringPrintf("--psk_file=%s", psk_file_.value().c_str()));
}
return true;
}
bool L2TPIPsecDriver::InitPEMOptions(std::vector<std::string>* options) {
std::vector<std::string> ca_certs;
if (args()->Contains<Strings>(kL2TPIPsecCaCertPemProperty)) {
ca_certs = args()->Get<Strings>(kL2TPIPsecCaCertPemProperty);
}
if (ca_certs.empty()) {
return false;
}
base::FilePath certfile = certificate_file_->CreatePEMFromStrings(ca_certs);
if (certfile.empty()) {
LOG(ERROR) << "Unable to extract certificates from PEM string.";
return false;
}
options->push_back(
base::StringPrintf("--server_ca_file=%s", certfile.value().c_str()));
return true;
}
bool L2TPIPsecDriver::InitXauthOptions(std::vector<std::string>* options,
Error* error) {
const auto user =
args()->Lookup<std::string>(kL2TPIPsecXauthUserProperty, "");
const auto password =
args()->Lookup<std::string>(kL2TPIPsecXauthPasswordProperty, "");
if (user.empty() && password.empty()) {
// Xauth credentials not configured.
return true;
}
if (user.empty() || password.empty()) {
Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
"XAUTH credentials are partially configured.");
return false;
}
const std::string xauth_credentials = user + "\n" + password + "\n";
if (!base::CreateTemporaryFileInDir(manager()->run_path(),
&xauth_credentials_file_) ||
!vpn_util_->WriteConfigFile(xauth_credentials_file_, xauth_credentials)) {
Error::PopulateAndLog(FROM_HERE, error, Error::kInternalError,
"Unable to setup XAUTH credentials file.");
return false;
}
options->push_back(base::StringPrintf(
"--xauth_credentials_file=%s", xauth_credentials_file_.value().c_str()));
return true;
}
bool L2TPIPsecDriver::AppendValueOption(const std::string& property,
const std::string& option,
std::vector<std::string>* options) {
const auto value = args()->Lookup<std::string>(property, "");
if (!value.empty()) {
options->push_back(
base::StringPrintf("%s=%s", option.c_str(), value.c_str()));
return true;
}
return false;
}
bool L2TPIPsecDriver::AppendFlag(const std::string& property,
const std::string& true_option,
const std::string& false_option,
std::vector<std::string>* options) {
const auto value = args()->Lookup<std::string>(property, "");
if (!value.empty()) {
options->push_back(value == "true" ? true_option : false_option);
return true;
}
return false;
}
void L2TPIPsecDriver::OnL2TPIPsecVPNDied(pid_t /*pid*/, int status) {
FailService(ExitStatusToFailure(status));
// TODO(petkov): Figure if we need to restart the connection.
}
void L2TPIPsecDriver::GetLogin(std::string* user, std::string* password) {
LOG(INFO) << "Login requested.";
const auto user_property =
args()->Lookup<std::string>(kL2TPIPsecUserProperty, "");
if (user_property.empty()) {
LOG(ERROR) << "User not set.";
return;
}
const std::string use_login_password =
args()->Lookup<std::string>(kL2TPIPsecUseLoginPasswordProperty, "");
if (use_login_password == "true") {
std::unique_ptr<password_provider::Password> login_password =
password_provider_->GetPassword();
if (login_password == nullptr || login_password->size() == 0) {
LOG(ERROR) << "Unable to retrieve user password";
return;
}
*user = user_property;
*password = std::string(login_password->GetRaw(), login_password->size());
return;
}
const auto password_property =
args()->Lookup<std::string>(kL2TPIPsecPasswordProperty, "");
if (password_property.empty()) {
LOG(ERROR) << "Password not set.";
return;
}
*user = user_property;
*password = password_property;
}
void L2TPIPsecDriver::Notify(const std::string& reason,
const std::map<std::string, std::string>& dict) {
LOG(INFO) << "IP configuration received: " << reason;
if (reason == kPPPReasonAuthenticating || reason == kPPPReasonAuthenticated) {
// These are uninteresting intermediate states that do not indicate failure.
return;
}
if (reason == kPPPReasonExit) {
// PPP failure is handled on the disconnect signal.
return;
}
if (reason != kPPPReasonConnect) {
DCHECK_EQ(kPPPReasonDisconnect, reason);
// TODO(crbug.com/989361) We should move into a disconnecting state, stop
// this task if it exists, and wait for the task to fully shut down before
// completing the disconnection. This should wait for the VPNDriver code to
// be refactored, as the disconnect flow is a mess as it stands.
external_task_.reset();
FailService(Service::kFailureUnknown);
return;
}
DeleteTemporaryFiles();
std::string interface_name = PPPDevice::GetInterfaceName(dict);
ip_properties_ = PPPDevice::ParseIPConfiguration(dict);
metrics()->SendSparseToUMA(Metrics::kMetricPPPMTUValue, ip_properties_.mtu);
// There is no IPv6 support for L2TP/IPsec VPN at this moment, so create a
// blackhole route for IPv6 traffic after establishing a IPv4 VPN.
// TODO(benchan): Generalize this when IPv6 support is added.
ip_properties_.blackhole_ipv6 = true;
// Reduce MTU to the minimum viable for IPv6, since the IPsec layer consumes
// some variable portion of the payload. Although this system does not yet
// support IPv6, it is a reasonable value to start with, since the minimum
// IPv6 packet size will plausibly be a size any gateway would support, and
// is also larger than the IPv4 minimum size.
ip_properties_.mtu = IPConfig::kMinIPv6MTU;
ip_properties_.method = kTypeVPN;
ReportConnectionMetrics();
// Make sure DeviceInfo is aware of this interface before invoking the
// connection success callback.
int interface_index = manager()->device_info()->GetIndex(interface_name);
if (interface_index != -1) {
OnLinkReady(interface_name, interface_index);
} else {
manager()->device_info()->AddVirtualInterfaceReadyCallback(
interface_name, base::BindOnce(&L2TPIPsecDriver::OnLinkReady,
weak_factory_.GetWeakPtr()));
}
}
void L2TPIPsecDriver::OnLinkReady(const std::string& link_name,
int interface_index) {
if (!event_handler_) {
LOG(ERROR) << "OnLinkReady() triggered in illegal service state";
return;
}
event_handler_->OnDriverConnected(link_name, interface_index);
}
bool L2TPIPsecDriver::IsPskRequired() const {
return const_args()->Lookup<std::string>(kL2TPIPsecPskProperty, "").empty() &&
const_args()
->Lookup<std::string>(kL2TPIPsecClientCertIdProperty, "")
.empty();
}
KeyValueStore L2TPIPsecDriver::GetProvider(Error* error) {
SLOG(this, 2) << __func__;
KeyValueStore props = VPNDriver::GetProvider(error);
props.Set<bool>(
kPassphraseRequiredProperty,
args()->Lookup<std::string>(kL2TPIPsecPasswordProperty, "").empty());
props.Set<bool>(kL2TPIPsecPskRequiredProperty, IsPskRequired());
return props;
}
void L2TPIPsecDriver::ReportConnectionMetrics() {
metrics()->SendEnumToUMA(Metrics::kMetricVpnDriver,
Metrics::kVpnDriverL2tpIpsec,
Metrics::kMetricVpnDriverMax);
// We output an enum for each of the authentication types specified,
// even if more than one is set at the same time.
bool has_remote_authentication = false;
if (args()->Contains<Strings>(kL2TPIPsecCaCertPemProperty) &&
!args()->Get<Strings>(kL2TPIPsecCaCertPemProperty).empty()) {
metrics()->SendEnumToUMA(
Metrics::kMetricVpnRemoteAuthenticationType,
Metrics::kVpnRemoteAuthenticationTypeL2tpIpsecCertificate,
Metrics::kMetricVpnRemoteAuthenticationTypeMax);
has_remote_authentication = true;
}
if (args()->Lookup<std::string>(kL2TPIPsecPskProperty, "") != "") {
metrics()->SendEnumToUMA(Metrics::kMetricVpnRemoteAuthenticationType,
Metrics::kVpnRemoteAuthenticationTypeL2tpIpsecPsk,
Metrics::kMetricVpnRemoteAuthenticationTypeMax);
has_remote_authentication = true;
}
if (!has_remote_authentication) {
metrics()->SendEnumToUMA(
Metrics::kMetricVpnRemoteAuthenticationType,
Metrics::kVpnRemoteAuthenticationTypeL2tpIpsecDefault,
Metrics::kMetricVpnRemoteAuthenticationTypeMax);
}
bool has_user_authentication = false;
if (args()->Lookup<std::string>(kL2TPIPsecClientCertIdProperty, "") != "") {
metrics()->SendEnumToUMA(
Metrics::kMetricVpnUserAuthenticationType,
Metrics::kVpnUserAuthenticationTypeL2tpIpsecCertificate,
Metrics::kMetricVpnUserAuthenticationTypeMax);
has_user_authentication = true;
}
if (args()->Lookup<std::string>(kL2TPIPsecPasswordProperty, "") != "" ||
args()->Lookup<std::string>(kL2TPIPsecUseLoginPasswordProperty, "") ==
"true") {
metrics()->SendEnumToUMA(
Metrics::kMetricVpnUserAuthenticationType,
Metrics::kVpnUserAuthenticationTypeL2tpIpsecUsernamePassword,
Metrics::kMetricVpnUserAuthenticationTypeMax);
has_user_authentication = true;
}
if (!has_user_authentication) {
metrics()->SendEnumToUMA(Metrics::kMetricVpnUserAuthenticationType,
Metrics::kVpnUserAuthenticationTypeL2tpIpsecNone,
Metrics::kMetricVpnUserAuthenticationTypeMax);
}
// Reports whether tunnel group is set or not (b/201478824).
const auto tunnel_group_usage =
args()->Lookup<std::string>(kL2TPIPsecTunnelGroupProperty, "") != ""
? Metrics::kVpnL2tpIpsecTunnelGroupUsageYes
: Metrics::kVpnL2tpIpsecTunnelGroupUsageNo;
metrics()->SendEnumToUMA(Metrics::kMetricVpnL2tpIpsecTunnelGroupUsage,
tunnel_group_usage,
Metrics::kMetricVpnL2tpIpsecTunnelGroupUsageMax);
// Reports cipher suites for IKE and ESP asynchronously.
int pid = process_manager()->StartProcessInMinijailWithStdout(
FROM_HERE, base::FilePath(kStrokePath), {"statusall"}, /*env=*/{},
VPNUtil::BuildMinijailOptions(/*capmask*/ 0),
base::BindOnce(&L2TPIPsecDriver::ParseCipherSuitesAndReport,
weak_factory_.GetWeakPtr()));
if (pid == -1) {
LOG(ERROR) << "Failed to run stroke to get the information of SA";
}
}
void L2TPIPsecDriver::ParseCipherSuitesAndReport(
int exit_status, const std::string& stdout_str) {
if (exit_status != 0) {
LOG(ERROR) << "stroke failed with " << exit_status;
return;
}
IPsecConnection::CipherSuite ike_cipher, esp_cipher;
if (!ParseStrokeStatusAllOutput(stdout_str, &ike_cipher, &esp_cipher)) {
return;
}
// Reports cipher suite for IKE.
metrics()->SendEnumToUMA(
Metrics::kMetricVpnL2tpIpsecIkeEncryptionAlgorithm,
std::get<0>(ike_cipher),
Metrics::kMetricVpnL2tpIpsecIkeEncryptionAlgorithmMax);
metrics()->SendEnumToUMA(
Metrics::kMetricVpnL2tpIpsecIkeIntegrityAlgorithm,
std::get<1>(ike_cipher),
Metrics::kMetricVpnL2tpIpsecIkeIntegrityAlgorithmMax);
metrics()->SendEnumToUMA(Metrics::kMetricVpnL2tpIpsecIkeDHGroup,
std::get<2>(ike_cipher),
Metrics::kMetricVpnL2tpIpsecIkeDHGroupMax);
// Reports cipher suite for ESP.
metrics()->SendEnumToUMA(
Metrics::kMetricVpnL2tpIpsecEspEncryptionAlgorithm,
std::get<0>(esp_cipher),
Metrics::kMetricVpnL2tpIpsecEspEncryptionAlgorithmMax);
metrics()->SendEnumToUMA(
Metrics::kMetricVpnL2tpIpsecEspIntegrityAlgorithm,
std::get<1>(esp_cipher),
Metrics::kMetricVpnL2tpIpsecEspIntegrityAlgorithmMax);
}
} // namespace shill