//
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "shill/vpn/openvpn_driver.h"
#include <algorithm>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#if defined(__ANDROID__)
#include <dbus/service_constants.h>
#else
#include <chromeos/dbus/service_constants.h>
#endif // __ANDROID__
#include <gtest/gtest.h>
#include "shill/error.h"
#include "shill/ipconfig.h"
#include "shill/logging.h"
#include "shill/mock_adaptors.h"
#include "shill/mock_certificate_file.h"
#include "shill/mock_device_info.h"
#include "shill/mock_event_dispatcher.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/mock_process_manager.h"
#include "shill/mock_service.h"
#include "shill/mock_store.h"
#include "shill/mock_virtual_device.h"
#include "shill/nice_mock_control.h"
#include "shill/rpc_task.h"
#include "shill/technology.h"
#include "shill/virtual_device.h"
#include "shill/vpn/mock_openvpn_management_server.h"
#include "shill/vpn/mock_vpn_service.h"
#include "shill/vpn/vpn_service.h"
using base::FilePath;
using base::WeakPtr;
using std::map;
using std::string;
using std::vector;
using testing::_;
using testing::AnyNumber;
using testing::DoAll;
using testing::ElementsAreArray;
using testing::Field;
using testing::Mock;
using testing::Ne;
using testing::NiceMock;
using testing::Return;
using testing::SetArgumentPointee;
using testing::StrictMock;
namespace shill {
struct AuthenticationExpectations {
AuthenticationExpectations()
: remote_authentication_type(Metrics::kVpnRemoteAuthenticationTypeMax) {}
AuthenticationExpectations(
const string& ca_cert_in,
const string& client_cert_in,
const string& user_in,
const string& otp_in,
const string& token_in,
Metrics::VpnRemoteAuthenticationType remote_authentication_type_in,
const vector<Metrics::VpnUserAuthenticationType>
&user_authentication_types_in)
: ca_cert(ca_cert_in),
client_cert(client_cert_in),
user(user_in),
otp(otp_in),
token(token_in),
remote_authentication_type(remote_authentication_type_in),
user_authentication_types(user_authentication_types_in) {}
string ca_cert;
string client_cert;
string user;
string otp;
string token;
Metrics::VpnRemoteAuthenticationType remote_authentication_type;
vector<Metrics::VpnUserAuthenticationType> user_authentication_types;
};
class OpenVPNDriverTest
: public testing::TestWithParam<AuthenticationExpectations>,
public RPCTaskDelegate {
public:
OpenVPNDriverTest()
: device_info_(&control_, &dispatcher_, &metrics_, &manager_),
metrics_(&dispatcher_),
manager_(&control_, &dispatcher_, &metrics_),
driver_(new OpenVPNDriver(&control_, &dispatcher_, &metrics_, &manager_,
&device_info_, &process_manager_)),
service_(new MockVPNService(&control_, &dispatcher_, &metrics_,
&manager_, driver_)),
device_(new MockVirtualDevice(
&control_, &dispatcher_, &metrics_, &manager_,
kInterfaceName, kInterfaceIndex, Technology::kVPN)),
certificate_file_(new MockCertificateFile()),
extra_certificates_file_(new MockCertificateFile()),
management_server_(new NiceMock<MockOpenVPNManagementServer>()) {
driver_->management_server_.reset(management_server_);
driver_->certificate_file_.reset(certificate_file_); // Passes ownership.
driver_->extra_certificates_file_.reset(
extra_certificates_file_); // Passes ownership.
CHECK(temporary_directory_.CreateUniqueTempDir());
driver_->openvpn_config_directory_ =
temporary_directory_.path().Append(kOpenVPNConfigDirectory);
}
virtual ~OpenVPNDriverTest() {}
virtual void TearDown() {
driver_->default_service_callback_tag_ = 0;
driver_->pid_ = 0;
driver_->device_ = nullptr;
driver_->service_ = nullptr;
if (!lsb_release_file_.empty()) {
EXPECT_TRUE(base::DeleteFile(lsb_release_file_, false));
lsb_release_file_.clear();
}
}
protected:
static const char kOption[];
static const char kProperty[];
static const char kValue[];
static const char kOption2[];
static const char kProperty2[];
static const char kValue2[];
static const char kGateway1[];
static const char kNetmask1[];
static const char kNetwork1[];
static const char kGateway2[];
static const char kNetmask2[];
static const char kNetwork2[];
static const char kInterfaceName[];
static const int kInterfaceIndex;
static const char kOpenVPNConfigDirectory[];
void SetArg(const string& arg, const string& value) {
driver_->args()->SetString(arg, value);
}
void SetArgArray(const string& arg, const vector<string>& value) {
driver_->args()->SetStrings(arg, value);
}
KeyValueStore* GetArgs() {
return driver_->args();
}
KeyValueStore GetProviderProperties(const PropertyStore& store) {
KeyValueStore props;
Error error;
EXPECT_TRUE(
store.GetKeyValueStoreProperty(kProviderProperty, &props, &error));
return props;
}
void RemoveStringArg(const string& arg) {
driver_->args()->RemoveString(arg);
}
const ServiceRefPtr& GetSelectedService() {
return device_->selected_service();
}
bool InitManagementChannelOptions(
vector<vector<string>>* options, Error* error) {
return driver_->InitManagementChannelOptions(options, error);
}
Sockets* GetSockets() {
return &driver_->sockets_;
}
void SetDevice(const VirtualDeviceRefPtr& device) {
driver_->device_ = device;
}
void SetService(const VPNServiceRefPtr& service) {
driver_->service_ = service;
}
VPNServiceRefPtr GetService() {
return driver_->service_;
}
void OnConnectionDisconnected() {
driver_->OnConnectionDisconnected();
}
void OnConnectTimeout() {
driver_->OnConnectTimeout();
}
void StartConnectTimeout(int timeout_seconds) {
driver_->StartConnectTimeout(timeout_seconds);
}
bool IsConnectTimeoutStarted() {
return driver_->IsConnectTimeoutStarted();
}
static int GetDefaultConnectTimeoutSeconds() {
return OpenVPNDriver::kDefaultConnectTimeoutSeconds;
}
static int GetReconnectOfflineTimeoutSeconds() {
return OpenVPNDriver::kReconnectOfflineTimeoutSeconds;
}
static int GetReconnectTLSErrorTimeoutSeconds() {
return OpenVPNDriver::kReconnectTLSErrorTimeoutSeconds;
}
static int GetReconnectTimeoutSeconds(OpenVPNDriver::ReconnectReason reason) {
return OpenVPNDriver::GetReconnectTimeoutSeconds(reason);
}
void SetClientState(const string& state) {
management_server_->state_ = state;
}
// Used to assert that a flag appears in the options.
void ExpectInFlags(const vector<vector<string>>& options, const string& flag);
void ExpectInFlags(const vector<vector<string>>& options, const string& flag,
const string& value);
void ExpectInFlags(const vector<vector<string>>& options,
const vector<string>& arguments);
void ExpectNotInFlags(const vector<vector<string>>& options,
const string& flag);
void SetupLSBRelease();
// Inherited from RPCTaskDelegate.
virtual void GetLogin(string* user, string* password);
virtual void Notify(const string& reason, const map<string, string>& dict);
NiceMockControl control_;
NiceMock<MockDeviceInfo> device_info_;
MockEventDispatcher dispatcher_;
MockMetrics metrics_;
MockProcessManager process_manager_;
MockManager manager_;
OpenVPNDriver* driver_; // Owned by |service_|.
scoped_refptr<MockVPNService> service_;
scoped_refptr<MockVirtualDevice> device_;
MockCertificateFile* certificate_file_; // Owned by |driver_|.
MockCertificateFile* extra_certificates_file_; // Owned by |driver_|.
base::ScopedTempDir temporary_directory_;
// Owned by |driver_|.
NiceMock<MockOpenVPNManagementServer>* management_server_;
FilePath lsb_release_file_;
};
const char OpenVPNDriverTest::kOption[] = "openvpn-option";
const char OpenVPNDriverTest::kProperty[] = "OpenVPN.SomeProperty";
const char OpenVPNDriverTest::kValue[] = "some-property-value";
const char OpenVPNDriverTest::kOption2[] = "openvpn-option2";
const char OpenVPNDriverTest::kProperty2[] = "OpenVPN.SomeProperty2";
const char OpenVPNDriverTest::kValue2[] = "some-property-value2";
const char OpenVPNDriverTest::kGateway1[] = "10.242.2.13";
const char OpenVPNDriverTest::kNetmask1[] = "255.255.255.255";
const char OpenVPNDriverTest::kNetwork1[] = "10.242.2.1";
const char OpenVPNDriverTest::kGateway2[] = "10.242.2.14";
const char OpenVPNDriverTest::kNetmask2[] = "255.255.0.0";
const char OpenVPNDriverTest::kNetwork2[] = "192.168.0.0";
const char OpenVPNDriverTest::kInterfaceName[] = "tun0";
const int OpenVPNDriverTest::kInterfaceIndex = 123;
const char OpenVPNDriverTest::kOpenVPNConfigDirectory[] = "openvpn";
void OpenVPNDriverTest::GetLogin(string* /*user*/, string* /*password*/) {}
void OpenVPNDriverTest::Notify(const string& /*reason*/,
const map<string, string>& /*dict*/) {}
void OpenVPNDriverTest::ExpectInFlags(const vector<vector<string>>& options,
const string& flag) {
ExpectInFlags(options, vector<string> { flag });
}
void OpenVPNDriverTest::ExpectInFlags(const vector<vector<string>>& options,
const string& flag,
const string& value) {
ExpectInFlags(options, vector<string> { flag, value });
}
void OpenVPNDriverTest::ExpectInFlags(const vector<vector<string>>& options,
const vector<string>& arguments) {
EXPECT_TRUE(std::find(options.begin(), options.end(), arguments) !=
options.end());
}
void OpenVPNDriverTest::ExpectNotInFlags(const vector<vector<string>>& options,
const string& flag) {
for (const auto& option : options) {
EXPECT_NE(flag, option[0]);
}
}
void OpenVPNDriverTest::SetupLSBRelease() {
static const char kLSBReleaseContents[] =
"\n"
"=\n"
"foo=\n"
"=bar\n"
"zoo==\n"
"CHROMEOS_RELEASE_BOARD=x86-alex\n"
"CHROMEOS_RELEASE_NAME=Chromium OS\n"
"CHROMEOS_RELEASE_VERSION=2202.0\n";
EXPECT_TRUE(base::CreateTemporaryFile(&lsb_release_file_));
EXPECT_EQ(arraysize(kLSBReleaseContents),
base::WriteFile(lsb_release_file_,
kLSBReleaseContents,
arraysize(kLSBReleaseContents)));
EXPECT_EQ(OpenVPNDriver::kLSBReleaseFile, driver_->lsb_release_file_.value());
driver_->lsb_release_file_ = lsb_release_file_;
}
TEST_F(OpenVPNDriverTest, Connect) {
EXPECT_CALL(*service_, SetState(Service::kStateConfiguring));
const string interface = kInterfaceName;
EXPECT_CALL(device_info_, CreateTunnelInterface(_))
.WillOnce(DoAll(SetArgumentPointee<0>(interface), Return(true)));
Error error;
driver_->Connect(service_, &error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(kInterfaceName, driver_->tunnel_interface_);
EXPECT_TRUE(driver_->IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, ConnectTunnelFailure) {
EXPECT_CALL(*service_, SetState(Service::kStateConfiguring));
EXPECT_CALL(device_info_, CreateTunnelInterface(_)).WillOnce(Return(false));
EXPECT_CALL(*service_, SetFailure(Service::kFailureInternal));
Error error;
driver_->Connect(service_, &error);
EXPECT_EQ(Error::kInternalError, error.type());
EXPECT_TRUE(driver_->tunnel_interface_.empty());
EXPECT_FALSE(driver_->IsConnectTimeoutStarted());
}
namespace {
MATCHER_P(IsIPAddress, address, "") {
IPAddress ip_address(IPAddress::kFamilyIPv4);
EXPECT_TRUE(ip_address.SetAddressFromString(address));
return ip_address.Equals(arg);
}
} // namespace
TEST_F(OpenVPNDriverTest, Notify) {
map<string, string> config;
driver_->service_ = service_;
driver_->device_ = device_;
StartConnectTimeout(0);
EXPECT_CALL(*device_,
UpdateIPConfig(Field(&IPConfig::Properties::address, "")));
driver_->Notify("up", config);
EXPECT_FALSE(driver_->IsConnectTimeoutStarted());
EXPECT_TRUE(GetSelectedService().get() == service_.get());
// Tests that existing properties are reused if no new ones provided.
driver_->ip_properties_.address = "1.2.3.4";
EXPECT_CALL(*device_,
UpdateIPConfig(Field(&IPConfig::Properties::address, "1.2.3.4")));
driver_->Notify("up", config);
}
TEST_P(OpenVPNDriverTest, NotifyUMA) {
map<string, string> config;
driver_->service_ = service_;
driver_->device_ = device_;
// Check that UMA metrics are emitted on Notify.
EXPECT_CALL(*device_, UpdateIPConfig(_));
EXPECT_CALL(metrics_, SendEnumToUMA(
Metrics::kMetricVpnDriver,
Metrics::kVpnDriverOpenVpn,
Metrics::kMetricVpnDriverMax));
EXPECT_CALL(metrics_, SendEnumToUMA(
Metrics::kMetricVpnRemoteAuthenticationType,
GetParam().remote_authentication_type,
Metrics::kVpnRemoteAuthenticationTypeMax));
for (const auto& authentication_type : GetParam().user_authentication_types) {
EXPECT_CALL(metrics_, SendEnumToUMA(
Metrics::kMetricVpnUserAuthenticationType,
authentication_type,
Metrics::kVpnUserAuthenticationTypeMax));
}
Error unused_error;
PropertyStore store;
driver_->InitPropertyStore(&store);
if (!GetParam().ca_cert.empty()) {
store.SetStringsProperty(kOpenVPNCaCertPemProperty,
vector<string>{ GetParam().ca_cert },
&unused_error);
}
if (!GetParam().client_cert.empty()) {
store.SetStringProperty(kOpenVPNClientCertIdProperty,
GetParam().client_cert,
&unused_error);
}
if (!GetParam().user.empty()) {
store.SetStringProperty(kOpenVPNUserProperty, GetParam().user,
&unused_error);
}
if (!GetParam().otp.empty()) {
store.SetStringProperty(kOpenVPNOTPProperty, GetParam().otp, &unused_error);
}
if (!GetParam().token.empty()) {
store.SetStringProperty(kOpenVPNTokenProperty, GetParam().token,
&unused_error);
}
driver_->Notify("up", config);
Mock::VerifyAndClearExpectations(&metrics_);
}
INSTANTIATE_TEST_CASE_P(
OpenVPNDriverAuthenticationTypes,
OpenVPNDriverTest,
::testing::Values(
AuthenticationExpectations(
"", "", "", "", "",
Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
vector<Metrics::VpnUserAuthenticationType> {
Metrics::kVpnUserAuthenticationTypeOpenVpnNone }),
AuthenticationExpectations(
"", "client_cert", "", "", "",
Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
vector<Metrics::VpnUserAuthenticationType> {
Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate }),
AuthenticationExpectations(
"", "client_cert", "user", "", "",
Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
vector<Metrics::VpnUserAuthenticationType> {
Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate,
Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword }),
AuthenticationExpectations(
"", "", "user", "", "",
Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
vector<Metrics::VpnUserAuthenticationType> {
Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword }),
AuthenticationExpectations(
"", "client_cert", "user", "otp", "",
Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
vector<Metrics::VpnUserAuthenticationType> {
Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate,
Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword,
Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp }),
AuthenticationExpectations(
"", "client_cert", "user", "otp", "token",
Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
vector<Metrics::VpnUserAuthenticationType> {
Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate,
Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword,
Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp,
Metrics::kVpnUserAuthenticationTypeOpenVpnUsernameToken }),
AuthenticationExpectations(
"ca_cert", "client_cert", "user", "otp", "token",
Metrics::kVpnRemoteAuthenticationTypeOpenVpnCertificate,
vector<Metrics::VpnUserAuthenticationType> {
Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate,
Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword,
Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp,
Metrics::kVpnUserAuthenticationTypeOpenVpnUsernameToken })));
TEST_F(OpenVPNDriverTest, NotifyFail) {
map<string, string> dict;
driver_->device_ = device_;
StartConnectTimeout(0);
EXPECT_CALL(*device_, DropConnection());
driver_->Notify("fail", dict);
EXPECT_TRUE(driver_->IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, GetRouteOptionEntry) {
OpenVPNDriver::RouteOptions routes;
EXPECT_EQ(nullptr, OpenVPNDriver::GetRouteOptionEntry("foo", "bar", &routes));
EXPECT_TRUE(routes.empty());
EXPECT_EQ(nullptr, OpenVPNDriver::GetRouteOptionEntry("foo", "foo", &routes));
EXPECT_TRUE(routes.empty());
EXPECT_EQ(nullptr,
OpenVPNDriver::GetRouteOptionEntry("foo", "fooz", &routes));
EXPECT_TRUE(routes.empty());
IPConfig::Route* route =
OpenVPNDriver::GetRouteOptionEntry("foo", "foo12", &routes);
EXPECT_EQ(1, routes.size());
EXPECT_EQ(route, &routes[12]);
route = OpenVPNDriver::GetRouteOptionEntry("foo", "foo13", &routes);
EXPECT_EQ(2, routes.size());
EXPECT_EQ(route, &routes[13]);
}
TEST_F(OpenVPNDriverTest, ParseRouteOption) {
OpenVPNDriver::RouteOptions routes;
OpenVPNDriver::ParseRouteOption("foo", "bar", &routes);
EXPECT_TRUE(routes.empty());
OpenVPNDriver::ParseRouteOption("gateway_2", kGateway2, &routes);
OpenVPNDriver::ParseRouteOption("netmask_2", kNetmask2, &routes);
OpenVPNDriver::ParseRouteOption("network_2", kNetwork2, &routes);
EXPECT_EQ(1, routes.size());
OpenVPNDriver::ParseRouteOption("gateway_1", kGateway1, &routes);
OpenVPNDriver::ParseRouteOption("netmask_1", kNetmask1, &routes);
OpenVPNDriver::ParseRouteOption("network_1", kNetwork1, &routes);
EXPECT_EQ(2, routes.size());
EXPECT_EQ(kGateway1, routes[1].gateway);
EXPECT_EQ(kNetmask1, routes[1].netmask);
EXPECT_EQ(kNetwork1, routes[1].host);
EXPECT_EQ(kGateway2, routes[2].gateway);
EXPECT_EQ(kNetmask2, routes[2].netmask);
EXPECT_EQ(kNetwork2, routes[2].host);
}
TEST_F(OpenVPNDriverTest, SetRoutes) {
OpenVPNDriver::RouteOptions routes;
routes[1].gateway = "1.2.3.4";
routes[1].host = "1.2.3.4";
routes[2].host = "2.3.4.5";
routes[2].netmask = "255.0.0.0";
routes[3].netmask = "255.0.0.0";
routes[3].gateway = "1.2.3.5";
routes[5].host = kNetwork2;
routes[5].netmask = kNetmask2;
routes[5].gateway = kGateway2;
routes[4].host = kNetwork1;
routes[4].netmask = kNetmask1;
routes[4].gateway = kGateway1;
IPConfig::Properties props;
OpenVPNDriver::SetRoutes(routes, &props);
ASSERT_EQ(2, props.routes.size());
EXPECT_EQ(kGateway1, props.routes[0].gateway);
EXPECT_EQ(kNetmask1, props.routes[0].netmask);
EXPECT_EQ(kNetwork1, props.routes[0].host);
EXPECT_EQ(kGateway2, props.routes[1].gateway);
EXPECT_EQ(kNetmask2, props.routes[1].netmask);
EXPECT_EQ(kNetwork2, props.routes[1].host);
// Tests that the routes are not reset if no new routes are supplied.
OpenVPNDriver::SetRoutes(OpenVPNDriver::RouteOptions(), &props);
EXPECT_EQ(2, props.routes.size());
}
TEST_F(OpenVPNDriverTest, SplitPortFromHost) {
string name, port;
EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("", nullptr, nullptr));
EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("", &name, &port));
EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com", &name, &port));
EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:", &name, &port));
EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost(":1234", &name, &port));
EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:f:1234", &name, &port));
EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:x", &name, &port));
EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:-1", &name, &port));
EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:+1", &name, &port));
EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:65536", &name, &port));
EXPECT_TRUE(OpenVPNDriver::SplitPortFromHost("v.com:0", &name, &port));
EXPECT_EQ("v.com", name);
EXPECT_EQ("0", port);
EXPECT_TRUE(OpenVPNDriver::SplitPortFromHost("w.com:65535", &name, &port));
EXPECT_EQ("w.com", name);
EXPECT_EQ("65535", port);
EXPECT_TRUE(OpenVPNDriver::SplitPortFromHost("x.com:12345", &name, &port));
EXPECT_EQ("x.com", name);
EXPECT_EQ("12345", port);
}
TEST_F(OpenVPNDriverTest, ParseForeignOption) {
vector<string> domain_search;
vector<string> dns_servers;
IPConfig::Properties props;
OpenVPNDriver::ParseForeignOption("", &domain_search, &dns_servers);
OpenVPNDriver::ParseForeignOption(
"dhcp-option DOMAIN", &domain_search, &dns_servers);
OpenVPNDriver::ParseForeignOption(
"dhcp-option DOMAIN zzz.com foo", &domain_search, &dns_servers);
OpenVPNDriver::ParseForeignOption(
"dhcp-Option DOmAIN xyz.com", &domain_search, &dns_servers);
ASSERT_EQ(1, domain_search.size());
EXPECT_EQ("xyz.com", domain_search[0]);
OpenVPNDriver::ParseForeignOption(
"dhcp-option DnS 1.2.3.4", &domain_search, &dns_servers);
ASSERT_EQ(1, dns_servers.size());
EXPECT_EQ("1.2.3.4", dns_servers[0]);
}
TEST_F(OpenVPNDriverTest, ParseForeignOptions) {
// This also tests that std::map is a sorted container.
map<int, string> options;
options[5] = "dhcp-option DOMAIN five.com";
options[2] = "dhcp-option DOMAIN two.com";
options[8] = "dhcp-option DOMAIN eight.com";
options[7] = "dhcp-option DOMAIN seven.com";
options[4] = "dhcp-option DOMAIN four.com";
options[10] = "dhcp-option dns 1.2.3.4";
IPConfig::Properties props;
OpenVPNDriver::ParseForeignOptions(options, &props);
ASSERT_EQ(5, props.domain_search.size());
EXPECT_EQ("two.com", props.domain_search[0]);
EXPECT_EQ("four.com", props.domain_search[1]);
EXPECT_EQ("five.com", props.domain_search[2]);
EXPECT_EQ("seven.com", props.domain_search[3]);
EXPECT_EQ("eight.com", props.domain_search[4]);
ASSERT_EQ(1, props.dns_servers.size());
EXPECT_EQ("1.2.3.4", props.dns_servers[0]);
// Test that the DNS properties are not updated if no new DNS properties are
// supplied.
OpenVPNDriver::ParseForeignOptions(map<int, string>(), &props);
EXPECT_EQ(5, props.domain_search.size());
ASSERT_EQ(1, props.dns_servers.size());
}
TEST_F(OpenVPNDriverTest, ParseIPConfiguration) {
map<string, string> config;
IPConfig::Properties props;
driver_->ParseIPConfiguration(config, &props);
EXPECT_EQ(IPAddress::kFamilyIPv4, props.address_family);
EXPECT_EQ(32, props.subnet_prefix);
props.subnet_prefix = 18;
driver_->ParseIPConfiguration(config, &props);
EXPECT_EQ(18, props.subnet_prefix);
// An "ifconfig_remote" parameter that looks like a netmask should be
// applied to the subnet prefix instead of to the peer address.
config["ifconfig_remotE"] = "255.255.0.0";
driver_->ParseIPConfiguration(config, &props);
EXPECT_EQ(16, props.subnet_prefix);
EXPECT_EQ("", props.peer_address);
config["ifconfig_loCal"] = "4.5.6.7";
config["ifconfiG_broadcast"] = "1.2.255.255";
config["ifconFig_netmAsk"] = "255.255.255.0";
config["ifconfig_remotE"] = "33.44.55.66";
config["route_vpN_gateway"] = "192.168.1.1";
config["trusted_ip"] = "99.88.77.66";
config["tun_mtu"] = "1000";
config["foreign_option_2"] = "dhcp-option DNS 4.4.4.4";
config["foreign_option_1"] = "dhcp-option DNS 1.1.1.1";
config["foreign_option_3"] = "dhcp-option DNS 2.2.2.2";
config["route_network_2"] = kNetwork2;
config["route_network_1"] = kNetwork1;
config["route_netmask_2"] = kNetmask2;
config["route_netmask_1"] = kNetmask1;
config["route_gateway_2"] = kGateway2;
config["route_gateway_1"] = kGateway1;
config["foo"] = "bar";
driver_->ParseIPConfiguration(config, &props);
EXPECT_EQ(IPAddress::kFamilyIPv4, props.address_family);
EXPECT_EQ("4.5.6.7", props.address);
EXPECT_EQ("1.2.255.255", props.broadcast_address);
EXPECT_EQ(24, props.subnet_prefix);
EXPECT_EQ("33.44.55.66", props.peer_address);
EXPECT_EQ("192.168.1.1", props.gateway);
EXPECT_EQ("99.88.77.66/32", props.exclusion_list[0]);
EXPECT_EQ(1, props.exclusion_list.size());
EXPECT_EQ(1000, props.mtu);
ASSERT_EQ(3, props.dns_servers.size());
EXPECT_EQ("1.1.1.1", props.dns_servers[0]);
EXPECT_EQ("4.4.4.4", props.dns_servers[1]);
EXPECT_EQ("2.2.2.2", props.dns_servers[2]);
ASSERT_EQ(2, props.routes.size());
EXPECT_EQ(kGateway1, props.routes[0].gateway);
EXPECT_EQ(kNetmask1, props.routes[0].netmask);
EXPECT_EQ(kNetwork1, props.routes[0].host);
EXPECT_EQ(kGateway2, props.routes[1].gateway);
EXPECT_EQ(kNetmask2, props.routes[1].netmask);
EXPECT_EQ(kNetwork2, props.routes[1].host);
EXPECT_FALSE(props.blackhole_ipv6);
// If the driver is configured to ignore the gateway provided, it will
// not set the "gateway" property for the properties, however the
// explicitly supplied routes should still be set.
SetArg(kOpenVPNIgnoreDefaultRouteProperty, "some value");
IPConfig::Properties props_without_gateway;
driver_->ParseIPConfiguration(config, &props_without_gateway);
EXPECT_EQ(kGateway1, props_without_gateway.routes[0].gateway);
EXPECT_EQ("", props_without_gateway.gateway);
// A pushed redirect flag should override the IgnoreDefaultRoute property.
config["redirect_gateway"] = "def1";
IPConfig::Properties props_with_override;
driver_->ParseIPConfiguration(config, &props_with_override);
EXPECT_EQ("192.168.1.1", props_with_override.gateway);
}
TEST_F(OpenVPNDriverTest, InitOptionsNoHost) {
Error error;
vector<vector<string>> options;
driver_->InitOptions(&options, &error);
EXPECT_EQ(Error::kInvalidArguments, error.type());
EXPECT_TRUE(options.empty());
}
TEST_F(OpenVPNDriverTest, InitOptions) {
static const char kHost[] = "192.168.2.254";
static const char kTLSAuthContents[] = "SOME-RANDOM-CONTENTS\n";
static const char kID[] = "TestPKCS11ID";
static const char kKU0[] = "00";
static const char kKU1[] = "01";
FilePath empty_cert;
SetArg(kProviderHostProperty, kHost);
SetArg(kOpenVPNTLSAuthContentsProperty, kTLSAuthContents);
SetArg(kOpenVPNClientCertIdProperty, kID);
SetArg(kOpenVPNRemoteCertKUProperty, string(kKU0) + " " + string(kKU1));
driver_->rpc_task_.reset(new RPCTask(&control_, this));
driver_->tunnel_interface_ = kInterfaceName;
EXPECT_CALL(*management_server_, Start(_, _, _)).WillOnce(Return(true));
EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(false));
Error error;
vector<vector<string>> options;
driver_->InitOptions(&options, &error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(vector<string> { "client" }, options[0]);
ExpectInFlags(options, "remote", kHost);
ExpectInFlags(options, vector<string> { "setenv", kRPCTaskPathVariable,
RPCTaskMockAdaptor::kRpcId });
ExpectInFlags(options, "dev", kInterfaceName);
ExpectInFlags(options, "group", "openvpn");
EXPECT_EQ(kInterfaceName, driver_->tunnel_interface_);
ASSERT_FALSE(driver_->tls_auth_file_.empty());
ExpectInFlags(options, "tls-auth", driver_->tls_auth_file_.value());
string contents;
EXPECT_TRUE(base::ReadFileToString(driver_->tls_auth_file_, &contents));
EXPECT_EQ(kTLSAuthContents, contents);
ExpectInFlags(options, "pkcs11-id", kID);
ExpectInFlags(options, "ca", OpenVPNDriver::kDefaultCACertificates);
ExpectInFlags(options, "syslog");
ExpectNotInFlags(options, "auth-user-pass");
ExpectInFlags(options, vector<string> { "remote-cert-ku", kKU0, kKU1 });
}
TEST_F(OpenVPNDriverTest, InitOptionsHostWithPort) {
SetArg(kProviderHostProperty, "v.com:1234");
driver_->rpc_task_.reset(new RPCTask(&control_, this));
driver_->tunnel_interface_ = kInterfaceName;
EXPECT_CALL(*management_server_, Start(_, _, _)).WillOnce(Return(true));
EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(false));
Error error;
vector<vector<string>> options;
driver_->InitOptions(&options, &error);
EXPECT_TRUE(error.IsSuccess());
ExpectInFlags(options, vector<string> { "remote", "v.com", "1234" });
}
TEST_F(OpenVPNDriverTest, InitCAOptions) {
static const char kHost[] = "192.168.2.254";
static const char kCaCert[] = "foo";
static const char kCaCertNSS[] = "{1234}";
Error error;
vector<vector<string>> options;
EXPECT_TRUE(driver_->InitCAOptions(&options, &error));
EXPECT_TRUE(error.IsSuccess());
ExpectInFlags(options, "ca", OpenVPNDriver::kDefaultCACertificates);
options.clear();
SetArg(kOpenVPNCaCertProperty, kCaCert);
EXPECT_TRUE(driver_->InitCAOptions(&options, &error));
ExpectInFlags(options, "ca", kCaCert);
EXPECT_TRUE(error.IsSuccess());
// We should ignore the CaCertNSS property.
SetArg(kOpenVPNCaCertNSSProperty, kCaCertNSS);
EXPECT_TRUE(driver_->InitCAOptions(&options, &error));
ExpectInFlags(options, "ca", kCaCert);
EXPECT_TRUE(error.IsSuccess());
SetArg(kOpenVPNCaCertProperty, "");
SetArg(kProviderHostProperty, kHost);
FilePath empty_cert;
error.Reset();
EXPECT_TRUE(driver_->InitCAOptions(&options, &error));
ExpectInFlags(options, "ca", OpenVPNDriver::kDefaultCACertificates);
EXPECT_TRUE(error.IsSuccess());
SetArg(kOpenVPNCaCertProperty, kCaCert);
const vector<string> kCaCertPEM{ "---PEM CONTENTS---" };
SetArgArray(kOpenVPNCaCertPemProperty, kCaCertPEM);
EXPECT_FALSE(driver_->InitCAOptions(&options, &error));
EXPECT_EQ(Error::kInvalidArguments, error.type());
EXPECT_EQ("Can't specify more than one of CACert and CACertPEM.",
error.message());
options.clear();
SetArg(kOpenVPNCaCertProperty, "");
SetArg(kProviderHostProperty, "");
static const char kPEMCertfile[] = "/tmp/pem-cert";
FilePath pem_cert(kPEMCertfile);
EXPECT_CALL(*certificate_file_, CreatePEMFromStrings(kCaCertPEM))
.WillOnce(Return(empty_cert))
.WillOnce(Return(pem_cert));
error.Reset();
EXPECT_FALSE(driver_->InitCAOptions(&options, &error));
EXPECT_EQ(Error::kInvalidArguments, error.type());
EXPECT_EQ("Unable to extract PEM CA certificates.", error.message());
error.Reset();
options.clear();
EXPECT_TRUE(driver_->InitCAOptions(&options, &error));
ExpectInFlags(options, "ca", kPEMCertfile);
EXPECT_TRUE(error.IsSuccess());
}
TEST_F(OpenVPNDriverTest, InitCertificateVerifyOptions) {
{
Error error;
vector<vector<string>> options;
// No options supplied.
driver_->InitCertificateVerifyOptions(&options);
EXPECT_TRUE(options.empty());
}
const char kName[] = "x509-name";
{
Error error;
vector<vector<string>> options;
// With Name property alone, we should have the 1-parameter version of the
// "x509-verify-name" parameter provided.
SetArg(kOpenVPNVerifyX509NameProperty, kName);
driver_->InitCertificateVerifyOptions(&options);
ExpectInFlags(options, "verify-x509-name", kName);
}
const char kType[] = "x509-type";
{
Error error;
vector<vector<string>> options;
// With both Name property and Type property set, we should have the
// 2-parameter version of the "x509-verify-name" parameter provided.
SetArg(kOpenVPNVerifyX509TypeProperty, kType);
driver_->InitCertificateVerifyOptions(&options);
ExpectInFlags(options, vector<string> { "verify-x509-name", kName, kType });
}
{
Error error;
vector<vector<string>> options;
// We should ignore the Type parameter if no Name parameter is specified.
SetArg(kOpenVPNVerifyX509NameProperty, "");
driver_->InitCertificateVerifyOptions(&options);
EXPECT_TRUE(options.empty());
}
}
TEST_F(OpenVPNDriverTest, InitClientAuthOptions) {
static const char kTestValue[] = "foo";
vector<vector<string>> options;
// No key or cert, assume user/password authentication.
driver_->InitClientAuthOptions(&options);
ExpectInFlags(options, "auth-user-pass");
ExpectNotInFlags(options, "key");
ExpectNotInFlags(options, "cert");
// Cert available, no user/password.
options.clear();
SetArg(kOpenVPNCertProperty, kTestValue);
driver_->InitClientAuthOptions(&options);
ExpectNotInFlags(options, "auth-user-pass");
ExpectNotInFlags(options, "key");
ExpectInFlags(options, "cert", kTestValue);
// Key available, no user/password.
options.clear();
SetArg(kOpenVPNKeyProperty, kTestValue);
driver_->InitClientAuthOptions(&options);
ExpectNotInFlags(options, "auth-user-pass");
ExpectInFlags(options, "key", kTestValue);
// Key available, AuthUserPass set.
options.clear();
SetArg(kOpenVPNAuthUserPassProperty, kTestValue);
driver_->InitClientAuthOptions(&options);
ExpectInFlags(options, "auth-user-pass");
ExpectInFlags(options, "key", kTestValue);
// Key available, User set.
options.clear();
RemoveStringArg(kOpenVPNAuthUserPassProperty);
SetArg(kOpenVPNUserProperty, "user");
driver_->InitClientAuthOptions(&options);
ExpectInFlags(options, "auth-user-pass");
ExpectInFlags(options, "key", kTestValue);
// Empty PKCS11 certificate id, no user/password/cert.
options.clear();
RemoveStringArg(kOpenVPNKeyProperty);
RemoveStringArg(kOpenVPNCertProperty);
RemoveStringArg(kOpenVPNUserProperty);
SetArg(kOpenVPNClientCertIdProperty, "");
driver_->InitClientAuthOptions(&options);
ExpectInFlags(options, "auth-user-pass");
ExpectNotInFlags(options, "key");
ExpectNotInFlags(options, "cert");
ExpectNotInFlags(options, "pkcs11-id");
// Non-empty PKCS11 certificate id, no user/password/cert.
options.clear();
SetArg(kOpenVPNClientCertIdProperty, kTestValue);
driver_->InitClientAuthOptions(&options);
ExpectNotInFlags(options, "auth-user-pass");
ExpectNotInFlags(options, "key");
ExpectNotInFlags(options, "cert");
// The "--pkcs11-id" option is added in InitPKCS11Options(), not here.
ExpectNotInFlags(options, "pkcs11-id");
// PKCS11 certificate id available, AuthUserPass set.
options.clear();
SetArg(kOpenVPNAuthUserPassProperty, kTestValue);
driver_->InitClientAuthOptions(&options);
ExpectInFlags(options, "auth-user-pass");
ExpectNotInFlags(options, "key");
ExpectNotInFlags(options, "cert");
// PKCS11 certificate id available, User set.
options.clear();
RemoveStringArg(kOpenVPNAuthUserPassProperty);
SetArg(kOpenVPNUserProperty, "user");
driver_->InitClientAuthOptions(&options);
ExpectInFlags(options, "auth-user-pass");
ExpectNotInFlags(options, "key");
ExpectNotInFlags(options, "cert");
}
TEST_F(OpenVPNDriverTest, InitExtraCertOptions) {
{
Error error;
vector<vector<string>> options;
// No ExtraCertOptions supplied.
EXPECT_TRUE(driver_->InitExtraCertOptions(&options, &error));
EXPECT_TRUE(error.IsSuccess());
EXPECT_TRUE(options.empty());
}
{
Error error;
vector<vector<string>> options;
SetArgArray(kOpenVPNExtraCertPemProperty, vector<string>());
// Empty ExtraCertOptions supplied.
EXPECT_TRUE(driver_->InitExtraCertOptions(&options, &error));
EXPECT_TRUE(error.IsSuccess());
EXPECT_TRUE(options.empty());
}
const vector<string> kExtraCerts{ "---PEM CONTENTS---" };
SetArgArray(kOpenVPNExtraCertPemProperty, kExtraCerts);
static const char kPEMCertfile[] = "/tmp/pem-cert";
FilePath pem_cert(kPEMCertfile);
EXPECT_CALL(*extra_certificates_file_, CreatePEMFromStrings(kExtraCerts))
.WillOnce(Return(FilePath()))
.WillOnce(Return(pem_cert));
// CreatePemFromStrings fails.
{
Error error;
vector<vector<string>> options;
EXPECT_FALSE(driver_->InitExtraCertOptions(&options, &error));
EXPECT_EQ(Error::kInvalidArguments, error.type());
EXPECT_TRUE(options.empty());
}
// CreatePemFromStrings succeeds.
{
Error error;
vector<vector<string>> options;
EXPECT_TRUE(driver_->InitExtraCertOptions(&options, &error));
EXPECT_TRUE(error.IsSuccess());
ExpectInFlags(options, "extra-certs", kPEMCertfile);
}
}
TEST_F(OpenVPNDriverTest, InitPKCS11Options) {
vector<vector<string>> options;
driver_->InitPKCS11Options(&options);
EXPECT_TRUE(options.empty());
static const char kID[] = "TestPKCS11ID";
SetArg(kOpenVPNClientCertIdProperty, kID);
driver_->InitPKCS11Options(&options);
ExpectInFlags(options, "pkcs11-id", kID);
ExpectInFlags(options, "pkcs11-providers", "libchaps.so");
static const char kProvider[] = "libpkcs11.so";
SetArg(kOpenVPNProviderProperty, kProvider);
options.clear();
driver_->InitPKCS11Options(&options);
ExpectInFlags(options, "pkcs11-id", kID);
ExpectInFlags(options, "pkcs11-providers", kProvider);
}
TEST_F(OpenVPNDriverTest, InitManagementChannelOptionsServerFail) {
vector<vector<string>> options;
EXPECT_CALL(*management_server_, Start(&dispatcher_, GetSockets(), &options))
.WillOnce(Return(false));
Error error;
EXPECT_FALSE(InitManagementChannelOptions(&options, &error));
EXPECT_EQ(Error::kInternalError, error.type());
EXPECT_EQ("Unable to setup management channel.", error.message());
}
TEST_F(OpenVPNDriverTest, InitManagementChannelOptionsOnline) {
vector<vector<string>> options;
EXPECT_CALL(*management_server_, Start(&dispatcher_, GetSockets(), &options))
.WillOnce(Return(true));
EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(true));
EXPECT_CALL(*management_server_, ReleaseHold());
Error error;
EXPECT_TRUE(InitManagementChannelOptions(&options, &error));
EXPECT_TRUE(error.IsSuccess());
}
TEST_F(OpenVPNDriverTest, InitManagementChannelOptionsOffline) {
vector<vector<string>> options;
EXPECT_CALL(*management_server_, Start(&dispatcher_, GetSockets(), &options))
.WillOnce(Return(true));
EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(false));
EXPECT_CALL(*management_server_, ReleaseHold()).Times(0);
Error error;
EXPECT_TRUE(InitManagementChannelOptions(&options, &error));
EXPECT_TRUE(error.IsSuccess());
}
TEST_F(OpenVPNDriverTest, InitLoggingOptions) {
vector<vector<string>> options;
bool vpn_logging = SLOG_IS_ON(VPN, 0);
ScopeLogger::GetInstance()->EnableScopesByName("-vpn");
driver_->InitLoggingOptions(&options);
ASSERT_EQ(1, options.size());
EXPECT_EQ(vector<string> { "syslog" }, options[0]);
ScopeLogger::GetInstance()->EnableScopesByName("+vpn");
options.clear();
driver_->InitLoggingOptions(&options);
ExpectInFlags(options, "verb", "3");
ScopeLogger::GetInstance()->EnableScopesByName("-vpn");
SetArg("OpenVPN.Verb", "2");
options.clear();
driver_->InitLoggingOptions(&options);
ExpectInFlags(options, "verb", "2");
ScopeLogger::GetInstance()->EnableScopesByName("+vpn");
SetArg("OpenVPN.Verb", "1");
options.clear();
driver_->InitLoggingOptions(&options);
ExpectInFlags(options, "verb", "1");
if (!vpn_logging) {
ScopeLogger::GetInstance()->EnableScopesByName("-vpn");
}
}
TEST_F(OpenVPNDriverTest, AppendValueOption) {
vector<vector<string>> options;
EXPECT_FALSE(
driver_->AppendValueOption("OpenVPN.UnknownProperty", kOption, &options));
EXPECT_TRUE(options.empty());
SetArg(kProperty, "");
EXPECT_FALSE(driver_->AppendValueOption(kProperty, kOption, &options));
EXPECT_TRUE(options.empty());
SetArg(kProperty, kValue);
SetArg(kProperty2, kValue2);
EXPECT_TRUE(driver_->AppendValueOption(kProperty, kOption, &options));
EXPECT_TRUE(driver_->AppendValueOption(kProperty2, kOption2, &options));
EXPECT_EQ(2, options.size());
vector<string> expected_value { kOption, kValue };
EXPECT_EQ(expected_value, options[0]);
vector<string> expected_value2 { kOption2, kValue2 };
EXPECT_EQ(expected_value2, options[1]);
}
TEST_F(OpenVPNDriverTest, AppendDelimitedValueOption) {
vector<vector<string>> options;
EXPECT_FALSE(
driver_->AppendDelimitedValueOption(
"OpenVPN.UnknownProperty", kOption, ' ', &options));
EXPECT_TRUE(options.empty());
SetArg(kProperty, "");
EXPECT_FALSE(
driver_->AppendDelimitedValueOption(kProperty, kOption, ' ', &options));
EXPECT_TRUE(options.empty());
string kConcatenatedValues(string(kValue) + " " + string(kValue2));
SetArg(kProperty, kConcatenatedValues);
SetArg(kProperty2, kConcatenatedValues);
EXPECT_TRUE(driver_->AppendDelimitedValueOption(
kProperty, kOption, ':', &options));
EXPECT_TRUE(driver_->AppendDelimitedValueOption(
kProperty2, kOption2, ' ', &options));
EXPECT_EQ(2, options.size());
vector<string> expected_value { kOption, kConcatenatedValues };
EXPECT_EQ(expected_value, options[0]);
vector<string> expected_value2 { kOption2, kValue, kValue2 };
EXPECT_EQ(expected_value2, options[1]);
}
TEST_F(OpenVPNDriverTest, AppendFlag) {
vector<vector<string>> options;
EXPECT_FALSE(
driver_->AppendFlag("OpenVPN.UnknownProperty", kOption, &options));
EXPECT_TRUE(options.empty());
SetArg(kProperty, "");
SetArg(kProperty2, kValue2);
EXPECT_TRUE(driver_->AppendFlag(kProperty, kOption, &options));
EXPECT_TRUE(driver_->AppendFlag(kProperty2, kOption2, &options));
EXPECT_EQ(2, options.size());
EXPECT_EQ(vector<string> { kOption }, options[0]);
EXPECT_EQ(vector<string> { kOption2 }, options[1]);
}
TEST_F(OpenVPNDriverTest, ClaimInterface) {
driver_->tunnel_interface_ = kInterfaceName;
EXPECT_FALSE(driver_->ClaimInterface(string(kInterfaceName) + "XXX",
kInterfaceIndex));
EXPECT_FALSE(driver_->device_);
static const char kHost[] = "192.168.2.254";
SetArg(kProviderHostProperty, kHost);
EXPECT_CALL(*management_server_, Start(_, _, _)).WillOnce(Return(true));
EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(false));
EXPECT_CALL(
process_manager_,
StartProcess(_, _, _, _, false /* Don't exit with parent */, _))
.WillOnce(Return(true));
const int kServiceCallbackTag = 1;
EXPECT_EQ(0, driver_->default_service_callback_tag_);
EXPECT_CALL(manager_, RegisterDefaultServiceCallback(_))
.WillOnce(Return(kServiceCallbackTag));
EXPECT_TRUE(driver_->ClaimInterface(kInterfaceName, kInterfaceIndex));
ASSERT_TRUE(driver_->device_);
EXPECT_EQ(kInterfaceIndex, driver_->device_->interface_index());
EXPECT_EQ(kServiceCallbackTag, driver_->default_service_callback_tag_);
}
TEST_F(OpenVPNDriverTest, IdleService) {
SetService(service_);
EXPECT_CALL(*service_, SetState(Service::kStateIdle));
driver_->IdleService();
}
TEST_F(OpenVPNDriverTest, FailService) {
static const char kErrorDetails[] = "Bad password.";
SetService(service_);
EXPECT_CALL(*service_, SetFailure(Service::kFailureConnect));
driver_->FailService(Service::kFailureConnect, kErrorDetails);
EXPECT_EQ(kErrorDetails, service_->error_details());
}
TEST_F(OpenVPNDriverTest, Cleanup) {
// Ensure no crash.
driver_->Cleanup(Service::kStateIdle,
Service::kFailureUnknown,
Service::kErrorDetailsNone);
const int kPID = 123456;
const int kServiceCallbackTag = 5;
static const char kErrorDetails[] = "Certificate revoked.";
driver_->default_service_callback_tag_ = kServiceCallbackTag;
driver_->pid_ = kPID;
driver_->rpc_task_.reset(new RPCTask(&control_, this));
driver_->tunnel_interface_ = kInterfaceName;
driver_->device_ = device_;
driver_->service_ = service_;
driver_->ip_properties_.address = "1.2.3.4";
StartConnectTimeout(0);
FilePath tls_auth_file;
EXPECT_TRUE(base::CreateTemporaryFile(&tls_auth_file));
EXPECT_FALSE(tls_auth_file.empty());
EXPECT_TRUE(base::PathExists(tls_auth_file));
driver_->tls_auth_file_ = tls_auth_file;
// Stop will be called twice -- once by Cleanup and once by the destructor.
EXPECT_CALL(*management_server_, Stop()).Times(2);
// UpdateExitCallback will be called twice -- once to ignore exit,
// and once to re-enabling monitoring of exit.
EXPECT_CALL(process_manager_, UpdateExitCallback(kPID, _)).Times(2);
EXPECT_CALL(manager_, DeregisterDefaultServiceCallback(kServiceCallbackTag));
EXPECT_CALL(process_manager_, StopProcess(kPID));
EXPECT_CALL(device_info_, DeleteInterface(_)).Times(0);
EXPECT_CALL(*device_, DropConnection());
EXPECT_CALL(*device_, SetEnabled(false));
EXPECT_CALL(*service_, SetFailure(Service::kFailureInternal));
driver_->Cleanup(
Service::kStateFailure, Service::kFailureInternal, kErrorDetails);
EXPECT_EQ(0, driver_->default_service_callback_tag_);
EXPECT_EQ(0, driver_->pid_);
EXPECT_FALSE(driver_->rpc_task_.get());
EXPECT_TRUE(driver_->tunnel_interface_.empty());
EXPECT_FALSE(driver_->device_);
EXPECT_FALSE(driver_->service_);
EXPECT_EQ(kErrorDetails, service_->error_details());
EXPECT_FALSE(base::PathExists(tls_auth_file));
EXPECT_TRUE(driver_->tls_auth_file_.empty());
EXPECT_TRUE(driver_->ip_properties_.address.empty());
EXPECT_FALSE(driver_->IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, SpawnOpenVPN) {
SetupLSBRelease();
EXPECT_FALSE(driver_->SpawnOpenVPN());
static const char kHost[] = "192.168.2.254";
SetArg(kProviderHostProperty, kHost);
driver_->tunnel_interface_ = "tun0";
driver_->rpc_task_.reset(new RPCTask(&control_, this));
EXPECT_CALL(*management_server_, Start(_, _, _))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(manager_, IsConnected()).Times(2).WillRepeatedly(Return(false));
const int kPID = 234678;
const map<string, string> expected_env{
{"IV_PLAT", "Chromium OS"},
{"IV_PLAT_REL", "2202.0"}};
EXPECT_CALL(process_manager_, StartProcess(_, _, _, expected_env, _, _))
.WillOnce(Return(-1))
.WillOnce(Return(kPID));
EXPECT_FALSE(driver_->SpawnOpenVPN());
EXPECT_TRUE(driver_->SpawnOpenVPN());
EXPECT_EQ(kPID, driver_->pid_);
}
TEST_F(OpenVPNDriverTest, OnOpenVPNDied) {
const int kPID = 99999;
driver_->device_ = device_;
driver_->pid_ = kPID;
EXPECT_CALL(*device_, DropConnection());
EXPECT_CALL(*device_, SetEnabled(false));
EXPECT_CALL(process_manager_, StopProcess(_)).Times(0);
EXPECT_CALL(device_info_, DeleteInterface(kInterfaceIndex));
driver_->OnOpenVPNDied(2);
EXPECT_EQ(0, driver_->pid_);
}
TEST_F(OpenVPNDriverTest, Disconnect) {
driver_->device_ = device_;
driver_->service_ = service_;
EXPECT_CALL(*device_, DropConnection());
EXPECT_CALL(*device_, SetEnabled(false));
EXPECT_CALL(device_info_, DeleteInterface(kInterfaceIndex));
EXPECT_CALL(*service_, SetState(Service::kStateIdle));
driver_->Disconnect();
EXPECT_FALSE(driver_->device_);
EXPECT_FALSE(driver_->service_);
}
TEST_F(OpenVPNDriverTest, OnConnectionDisconnected) {
EXPECT_CALL(*management_server_, Restart());
SetDevice(device_);
SetService(service_);
EXPECT_CALL(*device_, DropConnection());
EXPECT_CALL(*service_, SetState(Service::kStateAssociating));
OnConnectionDisconnected();
EXPECT_TRUE(IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, OnConnectTimeout) {
StartConnectTimeout(0);
SetService(service_);
EXPECT_CALL(*service_, SetFailure(Service::kFailureConnect));
OnConnectTimeout();
EXPECT_FALSE(GetService());
EXPECT_FALSE(IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, OnConnectTimeoutResolve) {
StartConnectTimeout(0);
SetService(service_);
SetClientState(OpenVPNManagementServer::kStateResolve);
EXPECT_CALL(*service_, SetFailure(Service::kFailureDNSLookup));
OnConnectTimeout();
EXPECT_FALSE(GetService());
EXPECT_FALSE(IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, OnReconnectingUnknown) {
EXPECT_FALSE(IsConnectTimeoutStarted());
EXPECT_CALL(dispatcher_,
PostDelayedTask(_, GetDefaultConnectTimeoutSeconds() * 1000));
SetDevice(device_);
SetService(service_);
EXPECT_CALL(*device_, DropConnection());
EXPECT_CALL(*service_, SetState(Service::kStateAssociating));
driver_->OnReconnecting(OpenVPNDriver::kReconnectReasonUnknown);
EXPECT_TRUE(IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, OnReconnectingTLSError) {
EXPECT_CALL(dispatcher_,
PostDelayedTask(_, GetReconnectOfflineTimeoutSeconds() * 1000));
EXPECT_CALL(dispatcher_,
PostDelayedTask(_, GetReconnectTLSErrorTimeoutSeconds() * 1000));
driver_->OnReconnecting(OpenVPNDriver::kReconnectReasonOffline);
EXPECT_TRUE(IsConnectTimeoutStarted());
// The scheduled timeout should not be affected for unknown reason.
driver_->OnReconnecting(OpenVPNDriver::kReconnectReasonUnknown);
EXPECT_TRUE(IsConnectTimeoutStarted());
// Reconnect on TLS error reschedules the timeout once.
driver_->OnReconnecting(OpenVPNDriver::kReconnectReasonTLSError);
EXPECT_TRUE(IsConnectTimeoutStarted());
driver_->OnReconnecting(OpenVPNDriver::kReconnectReasonTLSError);
EXPECT_TRUE(IsConnectTimeoutStarted());
}
TEST_F(OpenVPNDriverTest, InitPropertyStore) {
// Sanity test property store initialization.
PropertyStore store;
driver_->InitPropertyStore(&store);
const string kUser = "joe";
Error error;
EXPECT_TRUE(store.SetStringProperty(kOpenVPNUserProperty, kUser, &error));
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(kUser, GetArgs()->LookupString(kOpenVPNUserProperty, ""));
}
TEST_F(OpenVPNDriverTest, PassphraseRequired) {
PropertyStore store;
driver_->InitPropertyStore(&store);
KeyValueStore props = GetProviderProperties(store);
EXPECT_TRUE(props.LookupBool(kPassphraseRequiredProperty, false));
SetArg(kOpenVPNPasswordProperty, "random-password");
props = GetProviderProperties(store);
EXPECT_FALSE(props.LookupBool(kPassphraseRequiredProperty, true));
// This parameter should be write-only.
EXPECT_FALSE(props.ContainsString(kOpenVPNPasswordProperty));
SetArg(kOpenVPNPasswordProperty, "");
props = GetProviderProperties(store);
EXPECT_TRUE(props.LookupBool(kPassphraseRequiredProperty, false));
SetArg(kOpenVPNTokenProperty, "random-token");
props = GetProviderProperties(store);
EXPECT_FALSE(props.LookupBool(kPassphraseRequiredProperty, true));
// This parameter should be write-only.
EXPECT_FALSE(props.ContainsString(kOpenVPNTokenProperty));
}
TEST_F(OpenVPNDriverTest, GetEnvironment) {
SetupLSBRelease();
const map<string, string> expected{
{"IV_PLAT", "Chromium OS"},
{"IV_PLAT_REL", "2202.0"}};
ASSERT_EQ(expected, driver_->GetEnvironment());
EXPECT_EQ(0, base::WriteFile(lsb_release_file_, "", 0));
EXPECT_EQ(0, driver_->GetEnvironment().size());
}
TEST_F(OpenVPNDriverTest, OnOpenVPNExited) {
const int kExitStatus = 1;
std::unique_ptr<MockDeviceInfo> device_info(
new MockDeviceInfo(&control_, &dispatcher_, &metrics_, &manager_));
EXPECT_CALL(*device_info, DeleteInterface(kInterfaceIndex))
.WillOnce(Return(true));
WeakPtr<DeviceInfo> weak = device_info->AsWeakPtr();
EXPECT_TRUE(weak);
OpenVPNDriver::OnOpenVPNExited(weak, kInterfaceIndex, kExitStatus);
device_info.reset();
EXPECT_FALSE(weak);
// Expect no crash.
OpenVPNDriver::OnOpenVPNExited(weak, kInterfaceIndex, kExitStatus);
}
TEST_F(OpenVPNDriverTest, OnDefaultServiceChanged) {
driver_->service_ = service_;
ServiceRefPtr null_service;
EXPECT_CALL(*management_server_, Hold());
driver_->OnDefaultServiceChanged(null_service);
EXPECT_CALL(*management_server_, Hold());
driver_->OnDefaultServiceChanged(service_);
scoped_refptr<MockService> mock_service(
new MockService(&control_, &dispatcher_, &metrics_, &manager_));
EXPECT_CALL(*mock_service, IsConnected()).WillOnce(Return(false));
EXPECT_CALL(*management_server_, Hold());
driver_->OnDefaultServiceChanged(mock_service);
EXPECT_CALL(*mock_service, IsConnected()).WillOnce(Return(true));
EXPECT_CALL(*management_server_, ReleaseHold());
driver_->OnDefaultServiceChanged(mock_service);
}
TEST_F(OpenVPNDriverTest, GetReconnectTimeoutSeconds) {
EXPECT_EQ(GetDefaultConnectTimeoutSeconds(),
GetReconnectTimeoutSeconds(OpenVPNDriver::kReconnectReasonUnknown));
EXPECT_EQ(GetReconnectOfflineTimeoutSeconds(),
GetReconnectTimeoutSeconds(OpenVPNDriver::kReconnectReasonOffline));
EXPECT_EQ(GetReconnectTLSErrorTimeoutSeconds(),
GetReconnectTimeoutSeconds(
OpenVPNDriver::kReconnectReasonTLSError));
}
TEST_F(OpenVPNDriverTest, WriteConfigFile) {
const char kOption0[] = "option0";
const char kOption1[] = "option1";
const char kOption1Argument0[] = "option1-argument0";
const char kOption2[] = "option2";
const char kOption2Argument0[] = "option2-argument0\n\t\"'\\";
const char kOption2Argument0Transformed[] = "option2-argument0 \t\\\"'\\\\";
const char kOption2Argument1[] = "option2-argument1 space";
vector<vector<string>> options {
{ kOption0 },
{ kOption1, kOption1Argument0 },
{ kOption2, kOption2Argument0, kOption2Argument1 }
};
FilePath config_directory(
temporary_directory_.path().Append(kOpenVPNConfigDirectory));
FilePath config_file;
EXPECT_FALSE(base::PathExists(config_directory));
EXPECT_TRUE(driver_->WriteConfigFile(options, &config_file));
EXPECT_TRUE(base::PathExists(config_directory));
EXPECT_TRUE(base::PathExists(config_file));
EXPECT_TRUE(config_directory.IsParent(config_file));
string config_contents;
EXPECT_TRUE(base::ReadFileToString(config_file, &config_contents));
string expected_config_contents = base::StringPrintf(
"%s\n%s %s\n%s \"%s\" \"%s\"\n",
kOption0,
kOption1, kOption1Argument0,
kOption2, kOption2Argument0Transformed, kOption2Argument1);
EXPECT_EQ(expected_config_contents, config_contents);
}
} // namespace shill