// // 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/vpn_driver.h" #include <vector> #include <base/stl_util.h> #include <base/strings/string_number_conversions.h> #if defined(__ANDROID__) #include <dbus/service_constants.h> #else #include <chromeos/dbus/service_constants.h> #endif // __ANDROID__ #include <gmock/gmock.h> #include <gtest/gtest.h> #include "shill/mock_connection.h" #include "shill/mock_device_info.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" #include "shill/test_event_dispatcher.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 kVPNHostProperty[] = "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 }, { kVPNHostProperty, 0 }, { kL2tpIpsecCaCertPemProperty, Property::kArray }, { kOTPProperty, Property::kEphemeral }, { kPINProperty, Property::kWriteOnly }, { kPSKProperty, Property::kCredential }, { kPasswordProperty, Property::kCredential }, { kPortProperty, 0 }, { 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_), 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_; EventDispatcherForTest dispatcher_; MockMetrics metrics_; MockManager manager_; VPNDriverUnderTest driver_; }; bool VPNDriverTest::GetProviderPropertyString(const PropertyStore& store, const string& key, string* value) { KeyValueStore provider_properties; Error error; EXPECT_TRUE(store.GetKeyValueStoreProperty( kProviderProperty, &provider_properties, &error)); if (!provider_properties.ContainsString(key)) { return false; } if (value != nullptr) { *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( kProviderProperty, &provider_properties, &error)); if (!provider_properties.ContainsStrings(key)) { return false; } if (value != nullptr) { *value = provider_properties.GetStrings(key); } return true; } TEST_F(VPNDriverTest, Load) { MockStore storage; GetArgs()->SetString(kVPNHostProperty, "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(kVPNHostProperty)); EXPECT_FALSE(GetArgs()->ContainsStrings(kL2tpIpsecCaCertPemProperty)); EXPECT_FALSE(GetArgs()->ContainsString(kPSKProperty)); } TEST_F(VPNDriverTest, Save) { SetArg(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, 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, kProviderTypeProperty)) .Times(0); EXPECT_CALL(storage, DeleteKey(kStorageID, kL2tpIpsecCaCertPemProperty)); EXPECT_CALL(storage, DeleteKey(kStorageID, kPSKProperty)); EXPECT_CALL(storage, DeleteKey(kStorageID, kVPNHostProperty)); 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, nullptr, &error)); EXPECT_EQ(Error::kInvalidArguments, error.type()); } { Error error; EXPECT_FALSE( store.GetStringsProperty(kEapCaCertPemProperty, nullptr, &error)); EXPECT_EQ(Error::kInvalidArguments, error.type()); } EXPECT_FALSE(GetProviderPropertyString(store, kPortProperty, nullptr)); EXPECT_FALSE( GetProviderPropertyStrings(store, kEapCaCertPemProperty, nullptr)); const string kProviderType = "boo"; SetArg(kPortProperty, kPort); SetArg(kPasswordProperty, kPassword); SetArg(kProviderTypeProperty, kProviderType); SetArg(kVPNHostProperty, ""); 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, nullptr, &error)); EXPECT_EQ(Error::kInvalidArguments, error.type()); } { Error error; EXPECT_FALSE( store.GetStringsProperty(kEapCaCertPemProperty, nullptr, &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, kVPNHostProperty, &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, 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, nullptr)); } { Error error; EXPECT_TRUE(store.ClearProperty(kEapCaCertPemProperty, &error)); EXPECT_TRUE(error.IsSuccess()); EXPECT_FALSE(GetProviderPropertyStrings(store, kEapCaCertPemProperty, nullptr)); } // 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, nullptr)); // 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(nullptr); 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