blob: afb78c9361467e158ade0b7b043908db5c16388e [file] [log] [blame]
// Copyright (c) 2012 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.
#include "shill/vpn_driver.h"
#include <base/stl_util.h>
#include <base/string_number_conversions.h>
#include <chromeos/dbus/service_constants.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "shill/event_dispatcher.h"
#include "shill/glib.h"
#include "shill/mock_connection.h"
#include "shill/mock_device_info.h"
#include "shill/mock_glib.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/mock_service.h"
#include "shill/mock_store.h"
#include "shill/nice_mock_control.h"
#include "shill/property_store.h"
using std::string;
using std::vector;
using testing::_;
using testing::AnyNumber;
using testing::NiceMock;
using testing::Return;
using testing::SetArgumentPointee;
using testing::StrictMock;
using testing::Test;
namespace shill {
namespace {
const char kHostProperty[] = "VPN.Host";
const char kOTPProperty[] = "VPN.OTP";
const char kPINProperty[] = "VPN.PIN";
const char kPSKProperty[] = "VPN.PSK";
const char kPasswordProperty[] = "VPN.Password";
const char kPortProperty[] = "VPN.Port";
const char kPIN[] = "5555";
const char kPassword[] = "random-password";
const char kPort[] = "1234";
const char kStorageID[] = "vpn_service_id";
} // namespace
class VPNDriverUnderTest : public VPNDriver {
public:
VPNDriverUnderTest(EventDispatcher *dispatcher, Manager *manager);
virtual ~VPNDriverUnderTest() {}
// Inherited from VPNDriver.
MOCK_METHOD2(ClaimInterface, bool(const string &link_name,
int interface_index));
MOCK_METHOD2(Connect, void(const VPNServiceRefPtr &service, Error *error));
MOCK_METHOD0(Disconnect, void());
MOCK_METHOD0(OnConnectionDisconnected, void());
MOCK_CONST_METHOD0(GetProviderType, string());
private:
static const Property kProperties[];
DISALLOW_COPY_AND_ASSIGN(VPNDriverUnderTest);
};
// static
const VPNDriverUnderTest::Property VPNDriverUnderTest::kProperties[] = {
{ kEapCaCertPemProperty, Property::kArray },
{ kHostProperty, 0 },
{ kL2tpIpsecCaCertPemProperty, Property::kArray },
{ kOTPProperty, Property::kEphemeral },
{ kPINProperty, Property::kWriteOnly },
{ kPSKProperty, Property::kCredential },
{ kPasswordProperty, Property::kCredential },
{ kPortProperty, 0 },
{ flimflam::kProviderTypeProperty, 0 },
};
VPNDriverUnderTest::VPNDriverUnderTest(
EventDispatcher *dispatcher, Manager *manager)
: VPNDriver(dispatcher, manager, kProperties, arraysize(kProperties)) {}
class VPNDriverTest : public Test {
public:
VPNDriverTest()
: device_info_(&control_, &dispatcher_, &metrics_, &manager_),
metrics_(&dispatcher_),
manager_(&control_, &dispatcher_, &metrics_, &glib_),
driver_(&dispatcher_, &manager_) {}
virtual ~VPNDriverTest() {}
protected:
EventDispatcher *dispatcher() { return driver_.dispatcher_; }
void set_dispatcher(EventDispatcher *dispatcher) {
driver_.dispatcher_ = dispatcher;
}
const base::CancelableClosure &connect_timeout_callback() {
return driver_.connect_timeout_callback_;
}
bool IsConnectTimeoutStarted() { return driver_.IsConnectTimeoutStarted(); }
int connect_timeout_seconds() { return driver_.connect_timeout_seconds(); }
void StartConnectTimeout(int timeout_seconds) {
driver_.StartConnectTimeout(timeout_seconds);
}
void StopConnectTimeout() { driver_.StopConnectTimeout(); }
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(); }
bool GetProviderPropertyString(const PropertyStore &store,
const string &key,
string *value);
bool GetProviderPropertyStrings(const PropertyStore &store,
const string &key,
vector<string> *value);
NiceMockControl control_;
NiceMock<MockDeviceInfo> device_info_;
EventDispatcher dispatcher_;
MockMetrics metrics_;
MockGLib glib_;
MockManager manager_;
VPNDriverUnderTest driver_;
};
bool VPNDriverTest::GetProviderPropertyString(const PropertyStore &store,
const string &key,
string *value) {
KeyValueStore provider_properties;
Error error;
EXPECT_TRUE(store.GetKeyValueStoreProperty(
flimflam::kProviderProperty, &provider_properties, &error));
if (!provider_properties.ContainsString(key)) {
return false;
}
if (value != NULL) {
*value = provider_properties.GetString(key);
}
return true;
}
bool VPNDriverTest::GetProviderPropertyStrings(const PropertyStore &store,
const string &key,
vector<string> *value) {
KeyValueStore provider_properties;
Error error;
EXPECT_TRUE(store.GetKeyValueStoreProperty(
flimflam::kProviderProperty, &provider_properties, &error));
if (!provider_properties.ContainsStrings(key)) {
return false;
}
if (value != NULL) {
*value = provider_properties.GetStrings(key);
}
return true;
}
TEST_F(VPNDriverTest, Load) {
MockStore storage;
GetArgs()->SetString(kHostProperty, "1.2.3.4");
GetArgs()->SetString(kPSKProperty, "1234");
GetArgs()->SetStrings(kL2tpIpsecCaCertPemProperty,
vector<string>{ "cleared-cert0", "cleared-cert1" });
EXPECT_CALL(storage, GetString(kStorageID, _, _))
.WillRepeatedly(Return(false));
EXPECT_CALL(storage, GetStringList(kStorageID, _, _))
.WillRepeatedly(Return(false));
EXPECT_CALL(storage, GetString(_, kEapCaCertPemProperty, _)).Times(0);
EXPECT_CALL(storage, GetString(_, kOTPProperty, _)).Times(0);
EXPECT_CALL(storage, GetCryptedString(_, kOTPProperty, _)).Times(0);
EXPECT_CALL(storage, GetStringList(_, kOTPProperty, _)).Times(0);
vector<string> kCaCerts{ "cert0", "cert1" };
EXPECT_CALL(storage, GetStringList(kStorageID, kEapCaCertPemProperty, _))
.WillOnce(DoAll(SetArgumentPointee<2>(kCaCerts), Return(true)));
EXPECT_CALL(storage, GetString(kStorageID, kPortProperty, _))
.WillOnce(DoAll(SetArgumentPointee<2>(string(kPort)), Return(true)));
EXPECT_CALL(storage, GetString(kStorageID, kPINProperty, _))
.WillOnce(DoAll(SetArgumentPointee<2>(string(kPIN)), Return(true)));
EXPECT_CALL(storage, GetCryptedString(kStorageID, kPSKProperty, _))
.WillOnce(Return(false));
EXPECT_CALL(storage, GetCryptedString(kStorageID, kPasswordProperty, _))
.WillOnce(DoAll(SetArgumentPointee<2>(string(kPassword)), Return(true)));
EXPECT_TRUE(driver_.Load(&storage, kStorageID));
EXPECT_TRUE(GetArgs()->ContainsStrings(kEapCaCertPemProperty));
if (GetArgs()->ContainsStrings(kEapCaCertPemProperty)) {
EXPECT_EQ(kCaCerts, GetArgs()->GetStrings(kEapCaCertPemProperty));
}
EXPECT_EQ(kPort, GetArgs()->LookupString(kPortProperty, ""));
EXPECT_EQ(kPIN, GetArgs()->LookupString(kPINProperty, ""));
EXPECT_EQ(kPassword, GetArgs()->LookupString(kPasswordProperty, ""));
// Properties missing from the persistent store should be deleted.
EXPECT_FALSE(GetArgs()->ContainsString(kHostProperty));
EXPECT_FALSE(GetArgs()->ContainsStrings(kL2tpIpsecCaCertPemProperty));
EXPECT_FALSE(GetArgs()->ContainsString(kPSKProperty));
}
TEST_F(VPNDriverTest, Save) {
SetArg(flimflam::kProviderTypeProperty, "");
SetArg(kPINProperty, kPIN);
SetArg(kPortProperty, kPort);
SetArg(kPasswordProperty, kPassword);
SetArg(kOTPProperty, "987654");
const vector<string> kCaCerts{ "cert0", "cert1" };
SetArgArray(kEapCaCertPemProperty, kCaCerts);
MockStore storage;
EXPECT_CALL(storage,
SetStringList(kStorageID, kEapCaCertPemProperty, kCaCerts))
.WillOnce(Return(true));
EXPECT_CALL(storage,
SetString(kStorageID, flimflam::kProviderTypeProperty, ""))
.WillOnce(Return(true));
EXPECT_CALL(storage, SetString(kStorageID, kPortProperty, kPort))
.WillOnce(Return(true));
EXPECT_CALL(storage, SetString(kStorageID, kPINProperty, kPIN))
.WillOnce(Return(true));
EXPECT_CALL(storage,
SetCryptedString(kStorageID, kPasswordProperty, kPassword))
.WillOnce(Return(true));
EXPECT_CALL(storage, SetCryptedString(_, kOTPProperty, _)).Times(0);
EXPECT_CALL(storage, SetString(_, kOTPProperty, _)).Times(0);
EXPECT_CALL(storage, SetString(_, kEapCaCertPemProperty, _)).Times(0);
EXPECT_CALL(storage, DeleteKey(kStorageID, kEapCaCertPemProperty)).Times(0);
EXPECT_CALL(storage, DeleteKey(kStorageID, flimflam::kProviderTypeProperty))
.Times(0);
EXPECT_CALL(storage, DeleteKey(kStorageID, kL2tpIpsecCaCertPemProperty));
EXPECT_CALL(storage, DeleteKey(kStorageID, kPSKProperty));
EXPECT_CALL(storage, DeleteKey(kStorageID, kHostProperty));
EXPECT_TRUE(driver_.Save(&storage, kStorageID, true));
}
TEST_F(VPNDriverTest, SaveNoCredentials) {
SetArg(kPasswordProperty, kPassword);
SetArg(kPSKProperty, "");
MockStore storage;
EXPECT_CALL(storage, SetString(_, kPasswordProperty, _)).Times(0);
EXPECT_CALL(storage, SetCryptedString(_, kPasswordProperty, _)).Times(0);
EXPECT_CALL(storage, DeleteKey(kStorageID, _)).Times(AnyNumber());
EXPECT_CALL(storage, DeleteKey(kStorageID, kPasswordProperty));
EXPECT_CALL(storage, DeleteKey(kStorageID, kPSKProperty));
EXPECT_CALL(storage, DeleteKey(kStorageID, kEapCaCertPemProperty));
EXPECT_CALL(storage, DeleteKey(kStorageID, kL2tpIpsecCaCertPemProperty));
EXPECT_TRUE(driver_.Save(&storage, kStorageID, false));
}
TEST_F(VPNDriverTest, UnloadCredentials) {
SetArg(kOTPProperty, "654321");
SetArg(kPasswordProperty, kPassword);
SetArg(kPortProperty, kPort);
driver_.UnloadCredentials();
EXPECT_FALSE(GetArgs()->ContainsString(kOTPProperty));
EXPECT_FALSE(GetArgs()->ContainsString(kPasswordProperty));
EXPECT_EQ(kPort, GetArgs()->LookupString(kPortProperty, ""));
}
TEST_F(VPNDriverTest, InitPropertyStore) {
// Figure out if the store is actually hooked up to the driver argument
// KeyValueStore.
PropertyStore store;
driver_.InitPropertyStore(&store);
// An un-set property should not be readable.
{
Error error;
EXPECT_FALSE(store.GetStringProperty(kPortProperty, NULL, &error));
EXPECT_EQ(Error::kInvalidArguments, error.type());
}
{
Error error;
EXPECT_FALSE(store.GetStringsProperty(kEapCaCertPemProperty, NULL, &error));
EXPECT_EQ(Error::kInvalidArguments, error.type());
}
EXPECT_FALSE(GetProviderPropertyString(store, kPortProperty, NULL));
EXPECT_FALSE(GetProviderPropertyStrings(store, kEapCaCertPemProperty, NULL));
const string kProviderType = "boo";
SetArg(kPortProperty, kPort);
SetArg(kPasswordProperty, kPassword);
SetArg(flimflam::kProviderTypeProperty, kProviderType);
SetArg(kHostProperty, "");
const vector<string> kCaCerts{ "cert1" };
SetArgArray(kEapCaCertPemProperty, kCaCerts);
SetArgArray(kL2tpIpsecCaCertPemProperty, vector<string>());
// We should not be able to read a property out of the driver args using the
// key to the args directly.
{
Error error;
EXPECT_FALSE(store.GetStringProperty(kPortProperty, NULL, &error));
EXPECT_EQ(Error::kInvalidArguments, error.type());
}
{
Error error;
EXPECT_FALSE(store.GetStringsProperty(kEapCaCertPemProperty, NULL, &error));
EXPECT_EQ(Error::kInvalidArguments, error.type());
}
// We should instead be able to find it within the "Provider" stringmap.
{
string value;
EXPECT_TRUE(GetProviderPropertyString(store, kPortProperty, &value));
EXPECT_EQ(kPort, value);
}
{
vector<string> value;
EXPECT_TRUE(GetProviderPropertyStrings(store, kEapCaCertPemProperty,
&value));
EXPECT_EQ(kCaCerts, value);
}
// We should be able to read empty properties from the "Provider" stringmap.
{
string value;
EXPECT_TRUE(GetProviderPropertyString(store, kHostProperty, &value));
EXPECT_TRUE(value.empty());
}
{
vector<string> value;
EXPECT_TRUE(GetProviderPropertyStrings(store, kL2tpIpsecCaCertPemProperty,
&value));
EXPECT_TRUE(value.empty());
}
// Properties that start with the prefix "Provider." should be mapped to the
// name in the Properties dict with the prefix removed.
{
string value;
EXPECT_TRUE(GetProviderPropertyString(store, flimflam::kTypeProperty,
&value));
EXPECT_EQ(kProviderType, value);
}
// If we clear a property, we should no longer be able to find it.
{
Error error;
EXPECT_TRUE(store.ClearProperty(kPortProperty, &error));
EXPECT_TRUE(error.IsSuccess());
EXPECT_FALSE(GetProviderPropertyString(store, kPortProperty, NULL));
}
{
Error error;
EXPECT_TRUE(store.ClearProperty(kEapCaCertPemProperty, &error));
EXPECT_TRUE(error.IsSuccess());
EXPECT_FALSE(GetProviderPropertyStrings(store, kEapCaCertPemProperty,
NULL));
}
// A second attempt to clear this property should return an error.
{
Error error;
EXPECT_FALSE(store.ClearProperty(kPortProperty, &error));
EXPECT_EQ(Error::kNotFound, error.type());
}
{
Error error;
EXPECT_FALSE(store.ClearProperty(kEapCaCertPemProperty, &error));
EXPECT_EQ(Error::kNotFound, error.type());
}
// Test write only properties.
EXPECT_FALSE(GetProviderPropertyString(store, kPINProperty, NULL));
// Write properties to the driver args using the PropertyStore interface.
{
const string kValue = "some-value";
Error error;
EXPECT_TRUE(store.SetStringProperty(kPINProperty, kValue, &error));
EXPECT_EQ(kValue, GetArgs()->GetString(kPINProperty));
}
{
const vector<string> kValue{ "some-value" };
Error error;
EXPECT_TRUE(store.SetStringsProperty(kEapCaCertPemProperty, kValue,
&error));
EXPECT_EQ(kValue, GetArgs()->GetStrings(kEapCaCertPemProperty));
}
}
TEST_F(VPNDriverTest, ConnectTimeout) {
EXPECT_EQ(&dispatcher_, dispatcher());
EXPECT_TRUE(connect_timeout_callback().IsCancelled());
EXPECT_FALSE(IsConnectTimeoutStarted());
StartConnectTimeout(0);
EXPECT_FALSE(connect_timeout_callback().IsCancelled());
EXPECT_TRUE(IsConnectTimeoutStarted());
set_dispatcher(NULL);
StartConnectTimeout(0); // Expect no crash.
dispatcher_.DispatchPendingEvents();
EXPECT_TRUE(connect_timeout_callback().IsCancelled());
EXPECT_FALSE(IsConnectTimeoutStarted());
}
TEST_F(VPNDriverTest, StartStopConnectTimeout) {
EXPECT_FALSE(IsConnectTimeoutStarted());
EXPECT_EQ(0, connect_timeout_seconds());
const int kTimeout = 123;
StartConnectTimeout(kTimeout);
EXPECT_TRUE(IsConnectTimeoutStarted());
EXPECT_EQ(kTimeout, connect_timeout_seconds());
StartConnectTimeout(kTimeout - 20);
EXPECT_EQ(kTimeout, connect_timeout_seconds());
StopConnectTimeout();
EXPECT_FALSE(IsConnectTimeoutStarted());
EXPECT_EQ(0, connect_timeout_seconds());
}
} // namespace shill