blob: 751e22cb3ea820efc8f7ae5a146d31b273d9f918 [file] [log] [blame]
// Copyright (c) 2012 The Chromium 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 <cert.h>
#include <pk11pub.h>
#include <vector>
#include <string>
#include "base/at_exit.h"
#include "base/callback.h"
#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/path_service.h"
#include "base/scoped_temp_dir.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/network_library.h"
#include "chrome/browser/chromeos/cros/onc_network_parser.h"
#include "chrome/common/chrome_paths.h"
#include "crypto/nss_util.h"
#include "net/base/crypto_module.h"
#include "net/base/nss_cert_database.h"
#include "net/base/x509_certificate.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::Return;
namespace chromeos {
namespace {
// Have to do a stub here because MOCK can't handle closure arguments.
class StubEnrollmentDelegate : public EnrollmentDelegate {
public:
explicit StubEnrollmentDelegate(OncNetworkParser* parser)
: did_enroll(false), correct_args(false), parser_(parser) {}
void Enroll(const std::vector<std::string>& uri_list,
const base::Closure& closure) {
std::vector<std::string> expected_uri_list;
expected_uri_list.push_back("http://youtu.be/dQw4w9WgXcQ");
expected_uri_list.push_back("chrome-extension://abc/keygen-cert.html");
if (uri_list == expected_uri_list)
correct_args = true;
scoped_refptr<net::X509Certificate> certificate0(
parser_->ParseCertificate(0));
scoped_refptr<net::X509Certificate> certificate1(
parser_->ParseCertificate(1));
if (certificate0.get() && certificate1.get()) {
did_enroll = true;
closure.Run();
}
}
bool did_enroll;
bool correct_args;
OncNetworkParser* parser_;
};
void WifiNetworkConnectCallback(NetworkLibrary* cros, WifiNetwork* wifi) {
cros->ConnectToWifiNetwork(wifi);
}
void VirtualNetworkConnectCallback(NetworkLibrary* cros, VirtualNetwork* vpn) {
cros->ConnectToVirtualNetwork(vpn);
}
} // namespace
TEST(NetworkLibraryTest, DecodeNonAsciiSSID) {
// Sets network name.
{
std::string wifi_setname = "SSID TEST";
std::string wifi_setname_result = "SSID TEST";
WifiNetwork* wifi = new WifiNetwork("fw");
wifi->SetName(wifi_setname);
EXPECT_EQ(wifi->name(), wifi_setname_result);
delete wifi;
}
// Truncates invalid UTF-8
{
std::string wifi_setname2 = "SSID TEST \x01\xff!";
std::string wifi_setname2_result = "SSID TEST \xEF\xBF\xBD\xEF\xBF\xBD!";
WifiNetwork* wifi = new WifiNetwork("fw");
wifi->SetName(wifi_setname2);
EXPECT_EQ(wifi->name(), wifi_setname2_result);
delete wifi;
}
// UTF8 SSID
{
std::string wifi_utf8 = "UTF-8 \u3042\u3044\u3046";
std::string wifi_utf8_result = "UTF-8 \xE3\x81\x82\xE3\x81\x84\xE3\x81\x86";
WifiNetwork* wifi = new WifiNetwork("fw");
WifiNetwork::TestApi test_wifi(wifi);
test_wifi.SetSsid(wifi_utf8);
EXPECT_EQ(wifi->name(), wifi_utf8_result);
delete wifi;
}
// latin1 SSID -> UTF8 SSID
{
std::string wifi_latin1 = "latin-1 \xc0\xcb\xcc\xd6\xfb";
std::string wifi_latin1_result = "latin-1 \u00c0\u00cb\u00cc\u00d6\u00fb";
WifiNetwork* wifi = new WifiNetwork("fw");
WifiNetwork::TestApi test_wifi(wifi);
test_wifi.SetSsid(wifi_latin1);
EXPECT_EQ(wifi->name(), wifi_latin1_result);
delete wifi;
}
// Hex SSID
{
std::string wifi_hex = "5468697320697320484558205353494421";
std::string wifi_hex_result = "This is HEX SSID!";
WifiNetwork* wifi = new WifiNetwork("fw");
WifiNetwork::TestApi test_wifi(wifi);
test_wifi.SetHexSsid(wifi_hex);
EXPECT_EQ(wifi->name(), wifi_hex_result);
delete wifi;
}
}
// Create a stub libcros for testing NetworkLibrary functionality through
// NetworkLibraryStubImpl.
// NOTE: It would be of little value to test stub functions that simply return
// predefined values, e.g. ethernet_available(). However, many other functions
// such as connected_network() return values which are set indirectly and thus
// we can test the logic of those setters.
class NetworkLibraryStubTest : public testing::Test {
public:
NetworkLibraryStubTest() : cros_(NULL) {}
static void SetUpTestCase() {
// Ideally, we'd open a test DB for each test case, and close it
// again, removing the temp dir, but unfortunately, there's a
// bug in NSS that prevents this from working, so we just open
// it once, and empty it for each test case. Here's the bug:
// https://bugzilla.mozilla.org/show_bug.cgi?id=588269
ASSERT_TRUE(crypto::OpenTestNSSDB());
// There is no matching TearDownTestCase call to close the test NSS DB
// because that would leave NSS in a potentially broken state for further
// tests, due to https://bugzilla.mozilla.org/show_bug.cgi?id=588269
}
protected:
virtual void SetUp() {
slot_ = net::NSSCertDatabase::GetInstance()->GetPublicModule();
cros_ = CrosLibrary::Get()->GetNetworkLibrary();
ASSERT_TRUE(cros_) << "GetNetworkLibrary() Failed!";
// Test db should be empty at start of test.
EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
}
virtual void TearDown() {
cros_ = NULL;
EXPECT_TRUE(CleanupSlotContents(slot_->os_module_handle()));
EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
}
virtual void GetTestData(const std::string& filename, std::string* contents) {
FilePath path;
std::string error;
PathService::Get(chrome::DIR_TEST_DATA, &path);
path = path.AppendASCII("chromeos").AppendASCII("cros").Append(filename);
ASSERT_TRUE(contents != NULL);
ASSERT_TRUE(file_util::PathExists(path))
<< "Couldn't find test data file " << path.value();
ASSERT_TRUE(file_util::ReadFileToString(path, contents))
<< "Unable to read test data file " << path.value();
}
ScopedStubCrosEnabler cros_stub_;
NetworkLibrary* cros_;
private:
net::CertificateList ListCertsInSlot(PK11SlotInfo* slot) {
net::CertificateList result;
CERTCertList* cert_list = PK11_ListCertsInSlot(slot);
for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
!CERT_LIST_END(node, cert_list);
node = CERT_LIST_NEXT(node)) {
result.push_back(net::X509Certificate::CreateFromHandle(
node->cert, net::X509Certificate::OSCertHandles()));
}
CERT_DestroyCertList(cert_list);
// Sort the result so that test comparisons can be deterministic.
std::sort(result.begin(), result.end(), net::X509Certificate::LessThan());
return result;
}
bool CleanupSlotContents(PK11SlotInfo* slot) {
bool ok = true;
net::CertificateList certs = ListCertsInSlot(slot);
for (size_t i = 0; i < certs.size(); ++i) {
if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(certs[i]))
ok = false;
}
return ok;
}
scoped_refptr<net::CryptoModule> slot_;
};
// Default stub state:
// vpn1: disconnected, L2TP/IPsec + PSK
// vpn2: disconnected, L2TP/IPsec + user cert
// vpn3: disconnected, OpenVpn
// eth1: connected (active network)
// wifi1: connected
// wifi2: disconnected
// wifi3: disconnected, WEP
// wifi4: disconnected, 8021x
// wifi5: disconnected
// wifi6: disconnected
// cellular1: connected, activated, not roaming
// cellular2: disconnected, activated, roaming
TEST_F(NetworkLibraryStubTest, NetworkLibraryAccessors) {
// Set up state.
// Set wifi2->connecting for these tests.
WifiNetwork* wifi2 = cros_->FindWifiNetworkByPath("wifi2");
ASSERT_NE(static_cast<const Network*>(NULL), wifi2);
Network::TestApi test_wifi2(wifi2);
test_wifi2.SetConnecting();
// Set cellular1->connecting for these tests.
CellularNetwork* cellular1 = cros_->FindCellularNetworkByPath("cellular1");
ASSERT_NE(static_cast<const Network*>(NULL), cellular1);
Network::TestApi test_cellular1(cellular1);
test_cellular1.SetConnecting();
// Ethernet
ASSERT_NE(static_cast<const EthernetNetwork*>(NULL),
cros_->ethernet_network());
EXPECT_EQ("eth1", cros_->ethernet_network()->service_path());
EXPECT_NE(static_cast<const Network*>(NULL),
cros_->FindNetworkByPath("eth1"));
EXPECT_TRUE(cros_->ethernet_connected());
EXPECT_FALSE(cros_->ethernet_connecting());
// Wifi
ASSERT_NE(static_cast<const WifiNetwork*>(NULL), cros_->wifi_network());
EXPECT_EQ("wifi1", cros_->wifi_network()->service_path());
EXPECT_NE(static_cast<const WifiNetwork*>(NULL),
cros_->FindWifiNetworkByPath("wifi1"));
EXPECT_TRUE(cros_->wifi_connected());
EXPECT_FALSE(cros_->wifi_connecting()); // Only true for active wifi.
EXPECT_GE(cros_->wifi_networks().size(), 1U);
// Cellular
ASSERT_NE(static_cast<const CellularNetwork*>(NULL),
cros_->cellular_network());
EXPECT_EQ("cellular1", cros_->cellular_network()->service_path());
EXPECT_NE(static_cast<const CellularNetwork*>(NULL),
cros_->FindCellularNetworkByPath("cellular1"));
EXPECT_FALSE(cros_->cellular_connected());
EXPECT_TRUE(cros_->cellular_connecting());
EXPECT_GE(cros_->cellular_networks().size(), 1U);
// VPN
ASSERT_EQ(static_cast<const VirtualNetwork*>(NULL), cros_->virtual_network());
EXPECT_GE(cros_->virtual_networks().size(), 1U);
// Active network and global state
EXPECT_TRUE(cros_->Connected());
ASSERT_NE(static_cast<const Network*>(NULL), cros_->active_network());
EXPECT_EQ("eth1", cros_->active_network()->service_path());
ASSERT_NE(static_cast<const Network*>(NULL), cros_->connected_network());
EXPECT_EQ("eth1", cros_->connected_network()->service_path());
// The "wifi1" is connected, so we do not return "wifi2" for the connecting
// network. There is no conencted cellular network, so "cellular1" is
// returned by connecting_network().
EXPECT_TRUE(cros_->Connecting());
ASSERT_NE(static_cast<const Network*>(NULL), cros_->connecting_network());
EXPECT_EQ("cellular1", cros_->connecting_network()->service_path());
}
TEST_F(NetworkLibraryStubTest, NetworkConnectWifi) {
WifiNetwork* wifi1 = cros_->FindWifiNetworkByPath("wifi1");
ASSERT_NE(static_cast<const WifiNetwork*>(NULL), wifi1);
EXPECT_TRUE(wifi1->connected());
cros_->DisconnectFromNetwork(wifi1);
EXPECT_FALSE(wifi1->connected());
EXPECT_TRUE(cros_->CanConnectToNetwork(wifi1));
cros_->ConnectToWifiNetwork(wifi1);
EXPECT_TRUE(wifi1->connected());
}
TEST_F(NetworkLibraryStubTest, NetworkConnectOncWifi) {
// Import a wireless network via loading an ONC file.
std::string test_blob;
GetTestData("cert-pattern.onc", &test_blob);
OncNetworkParser parser(test_blob, "", NetworkUIData::ONC_SOURCE_USER_IMPORT);
ASSERT_TRUE(parser.parse_error().empty());
EXPECT_EQ(1, parser.GetNetworkConfigsSize());
EXPECT_EQ(2, parser.GetCertificatesSize());
scoped_ptr<Network> network(parser.ParseNetwork(0, NULL));
ASSERT_TRUE(network.get());
EXPECT_EQ(CLIENT_CERT_TYPE_PATTERN, network->client_cert_type());
StubEnrollmentDelegate* enrollment_delegate =
new StubEnrollmentDelegate(&parser);
network->SetEnrollmentDelegate(enrollment_delegate);
EXPECT_FALSE(enrollment_delegate->did_enroll);
EXPECT_FALSE(enrollment_delegate->correct_args);
WifiNetwork* wifi1 = static_cast<WifiNetwork*>(network.get());
ASSERT_NE(static_cast<const WifiNetwork*>(NULL), wifi1);
EXPECT_FALSE(wifi1->connected());
EXPECT_TRUE(cros_->CanConnectToNetwork(wifi1));
EXPECT_FALSE(wifi1->connected());
wifi1->AttemptConnection(
base::Bind(&WifiNetworkConnectCallback, cros_, wifi1));
EXPECT_TRUE(wifi1->connected());
EXPECT_TRUE(enrollment_delegate->did_enroll);
EXPECT_TRUE(enrollment_delegate->correct_args);
}
TEST_F(NetworkLibraryStubTest, NetworkConnectOncVPN) {
// Import a wireless network via loading an ONC file.
std::string test_blob;
GetTestData("cert-pattern-vpn.onc", &test_blob);
OncNetworkParser parser(test_blob, "", NetworkUIData::ONC_SOURCE_USER_IMPORT);
ASSERT_TRUE(parser.parse_error().empty());
EXPECT_EQ(1, parser.GetNetworkConfigsSize());
EXPECT_EQ(2, parser.GetCertificatesSize());
scoped_ptr<Network> network(parser.ParseNetwork(0, NULL));
ASSERT_TRUE(network.get());
EXPECT_EQ(CLIENT_CERT_TYPE_PATTERN, network->client_cert_type());
StubEnrollmentDelegate* enrollment_delegate =
new StubEnrollmentDelegate(&parser);
network->SetEnrollmentDelegate(enrollment_delegate);
EXPECT_FALSE(enrollment_delegate->did_enroll);
EXPECT_FALSE(enrollment_delegate->correct_args);
VirtualNetwork* vpn1 = static_cast<VirtualNetwork*>(network.get());
ASSERT_NE(static_cast<const VirtualNetwork*>(NULL), vpn1);
EXPECT_FALSE(vpn1->connected());
EXPECT_TRUE(cros_->CanConnectToNetwork(vpn1));
EXPECT_FALSE(vpn1->connected());
vpn1->AttemptConnection(
base::Bind(&VirtualNetworkConnectCallback, cros_, vpn1));
EXPECT_TRUE(vpn1->connected());
EXPECT_TRUE(enrollment_delegate->did_enroll);
EXPECT_TRUE(enrollment_delegate->correct_args);
}
TEST_F(NetworkLibraryStubTest, NetworkConnectVPN) {
VirtualNetwork* vpn1 = cros_->FindVirtualNetworkByPath("vpn1");
EXPECT_NE(static_cast<const VirtualNetwork*>(NULL), vpn1);
EXPECT_FALSE(vpn1->connected());
EXPECT_TRUE(cros_->CanConnectToNetwork(vpn1));
cros_->ConnectToVirtualNetwork(vpn1);
EXPECT_TRUE(vpn1->connected());
ASSERT_NE(static_cast<const VirtualNetwork*>(NULL), cros_->virtual_network());
EXPECT_EQ("vpn1", cros_->virtual_network()->service_path());
}
// TODO(stevenjb): Test remembered networks.
// TODO(stevenjb): Test network profiles.
// TODO(stevenjb): Test network devices.
// TODO(stevenjb): Test data plans.
// TODO(stevenjb): Test monitor network / device.
} // namespace chromeos