| // Copyright (c) 2011 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/wifi_service.h" |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include <base/string_util.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "shill/event_dispatcher.h" |
| #include "shill/manager.h" |
| #include "shill/mock_adaptors.h" |
| #include "shill/mock_control.h" |
| #include "shill/mock_service.h" |
| #include "shill/mock_store.h" |
| #include "shill/mock_wifi.h" |
| #include "shill/property_store_unittest.h" |
| #include "shill/wpa_supplicant.h" |
| |
| using std::map; |
| using std::string; |
| using std::vector; |
| |
| namespace shill { |
| using ::testing::_; |
| using ::testing::DoAll; |
| using ::testing::NiceMock; |
| using ::testing::Return; |
| using ::testing::SetArgumentPointee; |
| using ::testing::StrEq; |
| |
| class WiFiServiceTest : public PropertyStoreTest { |
| public: |
| WiFiServiceTest() : wifi_( |
| new NiceMock<MockWiFi>( |
| control_interface(), |
| dispatcher(), |
| manager(), |
| "wifi", |
| fake_mac, |
| 0)) {} |
| virtual ~WiFiServiceTest() {} |
| |
| protected: |
| static const char fake_mac[]; |
| scoped_refptr<MockWiFi> wifi() { return wifi_; } |
| |
| private: |
| scoped_refptr<MockWiFi> wifi_; |
| }; |
| |
| // static |
| const char WiFiServiceTest::fake_mac[] = "AaBBcCDDeeFF"; |
| |
| class WiFiServiceSecurityTest : public WiFiServiceTest { |
| public: |
| WiFiServiceRefPtr CreateServiceWithSecurity(const string &security) { |
| vector<uint8_t> ssid(5); |
| ssid.push_back(0xff); |
| |
| return new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| security, |
| false); |
| } |
| |
| bool TestStorageSecurityIs(WiFiServiceRefPtr wifi_service, |
| const string &security) { |
| string id = wifi_service->GetStorageIdentifier(); |
| size_t mac_pos = id.find(StringToLowerASCII(string(fake_mac))); |
| EXPECT_NE(mac_pos, string::npos); |
| size_t mode_pos = id.find(string(flimflam::kModeManaged), mac_pos); |
| EXPECT_NE(mode_pos, string::npos); |
| return id.find(string(security), mode_pos) != string::npos; |
| } |
| |
| // Test that a service that is created with security |from_security| |
| // gets by default a storage identifier with |to_security| as its |
| // security component. |
| bool TestStorageMapping(const string &from_security, |
| const string &to_security) { |
| WiFiServiceRefPtr wifi_service = CreateServiceWithSecurity(from_security); |
| return TestStorageSecurityIs(wifi_service, to_security); |
| } |
| |
| // Test whether a service of type |service_security| can load from a |
| // storage interface containing an entry for |storage_security|. |
| // Make sure the result meets |expectation|. If |expectation| is |
| // true, also make sure the service storage identifier changes to |
| // match |storage_security|. |
| bool TestLoadMapping(const string &service_security, |
| const string &storage_security, |
| bool expectation) { |
| WiFiServiceRefPtr wifi_service = |
| CreateServiceWithSecurity(service_security); |
| NiceMock<MockStore> mock_store; |
| const string storage_id = |
| wifi_service->GetStorageIdentifierForSecurity(storage_security); |
| EXPECT_CALL(mock_store, ContainsGroup(_)) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(mock_store, ContainsGroup(StrEq(storage_id))) |
| .WillRepeatedly(Return(true)); |
| bool is_loadable = wifi_service->IsLoadableFrom(&mock_store); |
| EXPECT_EQ(expectation, is_loadable); |
| bool is_loaded = wifi_service->Load(&mock_store); |
| EXPECT_EQ(expectation, is_loaded); |
| |
| if (expectation != is_loadable || expectation != is_loaded) { |
| return false; |
| } else if (!expectation) { |
| return true; |
| } else { |
| return TestStorageSecurityIs(wifi_service, storage_security); |
| } |
| } |
| }; |
| |
| TEST_F(WiFiServiceTest, StorageId) { |
| vector<uint8_t> ssid(5); |
| ssid.push_back(0xff); |
| |
| WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityNone, |
| false); |
| string id = wifi_service->GetStorageIdentifier(); |
| for (uint i = 0; i < id.length(); ++i) { |
| EXPECT_TRUE(id[i] == '_' || |
| isxdigit(id[i]) || |
| (isalpha(id[i]) && islower(id[i]))); |
| } |
| EXPECT_TRUE(wifi_service->TechnologyIs(Technology::kWifi)); |
| size_t mac_pos = id.find(StringToLowerASCII(string(fake_mac))); |
| EXPECT_NE(mac_pos, string::npos); |
| EXPECT_NE(id.find(string(flimflam::kModeManaged), mac_pos), string::npos); |
| } |
| |
| // Make sure the passphrase is registered as a write only property |
| // by reading and comparing all string properties returned on the store. |
| TEST_F(WiFiServiceTest, PassphraseWriteOnly) { |
| vector<uint8_t> ssid(5); |
| WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityWpa, |
| false); |
| ReadablePropertyConstIterator<string> it = |
| (wifi_service->store()).GetStringPropertiesIter(); |
| for( ; !it.AtEnd(); it.Advance()) |
| EXPECT_NE(it.Key(), flimflam::kPassphraseProperty); |
| } |
| |
| // Make sure setting the passphrase via D-Bus Service.SetProperty validates |
| // the passphrase. |
| TEST_F(WiFiServiceTest, PassphraseSetPropertyValidation) { |
| // We only spot check two password cases here to make sure the |
| // SetProperty code path does validation. We're not going to exhaustively |
| // test for all types of passwords. |
| vector<uint8_t> ssid(5); |
| WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityWep, |
| false); |
| Error error; |
| EXPECT_TRUE(wifi_service->mutable_store()->SetStringProperty( |
| flimflam::kPassphraseProperty, "0:abcde", &error)); |
| EXPECT_FALSE(wifi_service->mutable_store()->SetStringProperty( |
| flimflam::kPassphraseProperty, "invalid", &error)); |
| EXPECT_EQ(Error::kInvalidPassphrase, error.type()); |
| } |
| |
| TEST_F(WiFiServiceTest, PassphraseSetPropertyOpenNetwork) { |
| vector<uint8_t> ssid(5); |
| WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityNone, |
| false); |
| Error error; |
| EXPECT_FALSE(wifi_service->mutable_store()->SetStringProperty( |
| flimflam::kPassphraseProperty, "invalid", &error)); |
| EXPECT_EQ(Error::kNotSupported, error.type()); |
| } |
| |
| TEST_F(WiFiServiceTest, NonUTF8SSID) { |
| vector<uint8_t> ssid; |
| |
| ssid.push_back(0xff); // not a valid UTF-8 byte-sequence |
| WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityNone, |
| false); |
| map<string, ::DBus::Variant> properties; |
| // if service doesn't propertly sanitize SSID, this will generate SIGABRT. |
| DBusAdaptor::GetProperties(wifi_service->store(), &properties, NULL); |
| } |
| |
| MATCHER(WPASecurityArgs, "") { |
| return ContainsKey(arg, wpa_supplicant::kPropertySecurityProtocol) && |
| ContainsKey(arg, wpa_supplicant::kPropertyPreSharedKey); |
| } |
| |
| TEST_F(WiFiServiceTest, ConnectTaskWPA) { |
| vector<uint8_t> ssid(5); |
| WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityWpa, |
| false); |
| EXPECT_CALL(*wifi(), |
| ConnectTo(wifi_service.get(), WPASecurityArgs())); |
| wifi_service->ConnectTask(); |
| } |
| |
| TEST_F(WiFiServiceTest, ConnectTaskRSN) { |
| vector<uint8_t> ssid(5); |
| WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityRsn, |
| false); |
| EXPECT_CALL(*wifi(), |
| ConnectTo(wifi_service.get(), WPASecurityArgs())); |
| wifi_service->ConnectTask(); |
| } |
| |
| TEST_F(WiFiServiceTest, ConnectTaskPSK) { |
| vector<uint8_t> ssid(5); |
| WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityPsk, |
| false); |
| EXPECT_CALL(*wifi(), |
| ConnectTo(wifi_service.get(), WPASecurityArgs())); |
| wifi_service->ConnectTask(); |
| } |
| |
| MATCHER(WEPSecurityArgsKeyIndex0, "") { |
| return ContainsKey(arg, wpa_supplicant::kPropertyAuthAlg) && |
| ContainsKey(arg, wpa_supplicant::kPropertyWEPKey + std::string("0")) && |
| ContainsKey(arg, wpa_supplicant::kPropertyWEPTxKeyIndex) && |
| (arg.find(wpa_supplicant::kPropertyWEPTxKeyIndex)->second. |
| reader().get_uint32() == 0); |
| } |
| |
| MATCHER(WEPSecurityArgsKeyIndex1, "") { |
| return ContainsKey(arg, wpa_supplicant::kPropertyAuthAlg) && |
| ContainsKey(arg, wpa_supplicant::kPropertyWEPKey + std::string("1")) && |
| ContainsKey(arg, wpa_supplicant::kPropertyWEPTxKeyIndex) && |
| (arg.find(wpa_supplicant::kPropertyWEPTxKeyIndex)->second. |
| reader().get_uint32() == 1); |
| } |
| |
| MATCHER(WEPSecurityArgsKeyIndex2, "") { |
| return ContainsKey(arg, wpa_supplicant::kPropertyAuthAlg) && |
| ContainsKey(arg, wpa_supplicant::kPropertyWEPKey + std::string("2")) && |
| ContainsKey(arg, wpa_supplicant::kPropertyWEPTxKeyIndex) && |
| (arg.find(wpa_supplicant::kPropertyWEPTxKeyIndex)->second. |
| reader().get_uint32() == 2); |
| } |
| |
| MATCHER(WEPSecurityArgsKeyIndex3, "") { |
| return ContainsKey(arg, wpa_supplicant::kPropertyAuthAlg) && |
| ContainsKey(arg, wpa_supplicant::kPropertyWEPKey + std::string("3")) && |
| ContainsKey(arg, wpa_supplicant::kPropertyWEPTxKeyIndex) && |
| (arg.find(wpa_supplicant::kPropertyWEPTxKeyIndex)->second. |
| reader().get_uint32() == 3); |
| } |
| |
| TEST_F(WiFiServiceTest, ConnectTaskWEP) { |
| vector<uint8_t> ssid(5); |
| WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityWep, |
| false); |
| Error error; |
| wifi_service->SetPassphrase("0:abcdefghijklm", &error); |
| EXPECT_CALL(*wifi(), |
| ConnectTo(wifi_service.get(), WEPSecurityArgsKeyIndex0())); |
| wifi_service->ConnectTask(); |
| |
| wifi_service->SetPassphrase("abcdefghijklm", &error); |
| EXPECT_CALL(*wifi(), |
| ConnectTo(wifi_service.get(), WEPSecurityArgsKeyIndex0())); |
| wifi_service->ConnectTask(); |
| |
| wifi_service->SetPassphrase("1:abcdefghijklm", &error); |
| EXPECT_CALL(*wifi(), |
| ConnectTo(wifi_service.get(), WEPSecurityArgsKeyIndex1())); |
| wifi_service->ConnectTask(); |
| |
| wifi_service->SetPassphrase("2:abcdefghijklm", &error); |
| EXPECT_CALL(*wifi(), |
| ConnectTo(wifi_service.get(), WEPSecurityArgsKeyIndex2())); |
| wifi_service->ConnectTask(); |
| |
| wifi_service->SetPassphrase("3:abcdefghijklm", &error); |
| EXPECT_CALL(*wifi(), |
| ConnectTo(wifi_service.get(), WEPSecurityArgsKeyIndex3())); |
| wifi_service->ConnectTask(); |
| } |
| |
| TEST_F(WiFiServiceTest, LoadHidden) { |
| vector<uint8_t> ssid(5); |
| ssid.push_back(0xff); |
| |
| WiFiServiceRefPtr service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityNone, |
| false); |
| ASSERT_FALSE(service->hidden_ssid_); |
| NiceMock<MockStore> mock_store; |
| const string storage_id = service->GetStorageIdentifier(); |
| EXPECT_CALL(mock_store, ContainsGroup(StrEq(storage_id))) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(mock_store, GetBool(_, _, _)) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(mock_store, |
| GetBool(StrEq(storage_id), WiFiService::kStorageHiddenSSID, _)) |
| .WillRepeatedly(DoAll(SetArgumentPointee<2>(true), Return(true))); |
| EXPECT_TRUE(service->Load(&mock_store)); |
| EXPECT_TRUE(service->hidden_ssid_); |
| } |
| |
| TEST_F(WiFiServiceSecurityTest, WPAMapping) { |
| EXPECT_TRUE(TestStorageMapping(flimflam::kSecurityRsn, |
| flimflam::kSecurityPsk)); |
| EXPECT_TRUE(TestStorageMapping(flimflam::kSecurityWpa, |
| flimflam::kSecurityPsk)); |
| EXPECT_TRUE(TestStorageMapping(flimflam::kSecurityPsk, |
| flimflam::kSecurityPsk)); |
| EXPECT_TRUE(TestStorageMapping(flimflam::kSecurityWep, |
| flimflam::kSecurityWep)); |
| EXPECT_TRUE(TestStorageMapping(flimflam::kSecurityNone, |
| flimflam::kSecurityNone)); |
| // TODO(pstew): 802.1x is in a NOTIMPLEMENTED block in wifi_service.cc |
| // EXPECT_TRUE(TestStorageMapping(flimflam::kSecurity8021x, |
| // flimflam::kSecurity8021x)); |
| } |
| |
| TEST_F(WiFiServiceSecurityTest, LoadMapping) { |
| EXPECT_TRUE(TestLoadMapping(flimflam::kSecurityRsn, |
| flimflam::kSecurityPsk, |
| true)); |
| EXPECT_TRUE(TestLoadMapping(flimflam::kSecurityRsn, |
| flimflam::kSecurityRsn, |
| true)); |
| EXPECT_TRUE(TestLoadMapping(flimflam::kSecurityRsn, |
| flimflam::kSecurityWpa, |
| false)); |
| EXPECT_TRUE(TestLoadMapping(flimflam::kSecurityWpa, |
| flimflam::kSecurityPsk, |
| true)); |
| EXPECT_TRUE(TestLoadMapping(flimflam::kSecurityWpa, |
| flimflam::kSecurityWpa, |
| true)); |
| EXPECT_TRUE(TestLoadMapping(flimflam::kSecurityWpa, |
| flimflam::kSecurityRsn, |
| false)); |
| EXPECT_TRUE(TestLoadMapping(flimflam::kSecurityWep, |
| flimflam::kSecurityWep, |
| true)); |
| EXPECT_TRUE(TestLoadMapping(flimflam::kSecurityWep, |
| flimflam::kSecurityPsk, |
| false)); |
| } |
| |
| TEST_F(WiFiServiceTest, ParseStorageIdentifier) { |
| vector<uint8_t> ssid(5); |
| ssid.push_back(0xff); |
| |
| WiFiServiceRefPtr service = new WiFiService(control_interface(), |
| dispatcher(), |
| manager(), |
| wifi(), |
| ssid, |
| flimflam::kModeManaged, |
| flimflam::kSecurityNone, |
| false); |
| const string storage_id = service->GetStorageIdentifier(); |
| string address; |
| string mode; |
| string security; |
| EXPECT_TRUE(service->ParseStorageIdentifier(storage_id, &address, &mode, |
| &security)); |
| EXPECT_EQ(StringToLowerASCII(string(fake_mac)), address); |
| EXPECT_EQ(flimflam::kModeManaged, mode); |
| EXPECT_EQ(flimflam::kSecurityNone, security); |
| } |
| |
| } // namespace shill |