network_connection_handler_unittest.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromeos/network/network_connection_handler.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/file_util.h"
10#include "base/json/json_reader.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/message_loop/message_loop.h"
13#include "base/run_loop.h"
14#include "base/strings/stringprintf.h"
15#include "chromeos/cert_loader.h"
16#include "chromeos/dbus/fake_dbus_thread_manager.h"
17#include "chromeos/dbus/shill_device_client.h"
18#include "chromeos/dbus/shill_manager_client.h"
19#include "chromeos/dbus/shill_profile_client.h"
20#include "chromeos/dbus/shill_service_client.h"
21#include "chromeos/network/managed_network_configuration_handler_impl.h"
22#include "chromeos/network/network_configuration_handler.h"
23#include "chromeos/network/network_profile_handler.h"
24#include "chromeos/network/network_state_handler.h"
25#include "chromeos/network/onc/onc_utils.h"
26#include "chromeos/tpm_token_loader.h"
27#include "components/onc/onc_constants.h"
28#include "crypto/nss_util.h"
29#include "crypto/nss_util_internal.h"
30#include "net/base/net_errors.h"
31#include "net/base/test_data_directory.h"
32#include "net/cert/nss_cert_database_chromeos.h"
33#include "net/cert/x509_certificate.h"
34#include "net/test/cert_test_util.h"
35#include "testing/gtest/include/gtest/gtest.h"
36#include "third_party/cros_system_api/dbus/service_constants.h"
37
38namespace {
39
40const char* kSuccessResult = "success";
41
42void ConfigureCallback(const dbus::ObjectPath& result) {
43}
44
45void ConfigureErrorCallback(const std::string& error_name,
46                            const std::string& error_message) {
47}
48
49}  // namespace
50
51namespace chromeos {
52
53class NetworkConnectionHandlerTest : public testing::Test {
54 public:
55  NetworkConnectionHandlerTest()
56      : user_("userhash"),
57        test_manager_client_(NULL),
58        test_service_client_(NULL) {}
59
60  virtual ~NetworkConnectionHandlerTest() {
61  }
62
63  virtual void SetUp() OVERRIDE {
64    ASSERT_TRUE(user_.constructed_successfully());
65    user_.FinishInit();
66
67    test_nssdb_.reset(new net::NSSCertDatabaseChromeOS(
68        crypto::GetPublicSlotForChromeOSUser(user_.username_hash()),
69        crypto::GetPrivateSlotForChromeOSUser(
70            user_.username_hash(),
71            base::Callback<void(crypto::ScopedPK11Slot)>())));
72    test_nssdb_->SetSlowTaskRunnerForTest(message_loop_.message_loop_proxy());
73
74    TPMTokenLoader::InitializeForTest();
75
76    CertLoader::Initialize();
77    CertLoader* cert_loader = CertLoader::Get();
78    cert_loader->force_hardware_backed_for_test();
79
80    FakeDBusThreadManager* dbus_manager = new FakeDBusThreadManager;
81    dbus_manager->SetFakeClients();
82    DBusThreadManager::InitializeForTesting(dbus_manager);
83    test_manager_client_ =
84        dbus_manager->GetShillManagerClient()->GetTestInterface();
85    test_service_client_ =
86        dbus_manager->GetShillServiceClient()->GetTestInterface();
87
88    test_manager_client_->AddTechnology(shill::kTypeWifi, true /* enabled */);
89    dbus_manager->GetShillDeviceClient()->GetTestInterface()->AddDevice(
90        "/device/wifi1", shill::kTypeWifi, "wifi_device1");
91    test_manager_client_->AddTechnology(shill::kTypeCellular,
92                                        true /* enabled */);
93    dbus_manager->GetShillProfileClient()->GetTestInterface()->AddProfile(
94        "profile_path", std::string() /* shared profile */);
95
96    base::RunLoop().RunUntilIdle();
97    LoginState::Initialize();
98    network_state_handler_.reset(NetworkStateHandler::InitializeForTest());
99    network_config_handler_.reset(
100        NetworkConfigurationHandler::InitializeForTest(
101            network_state_handler_.get()));
102
103    network_profile_handler_.reset(new NetworkProfileHandler());
104    network_profile_handler_->Init();
105
106    managed_config_handler_.reset(new ManagedNetworkConfigurationHandlerImpl());
107    managed_config_handler_->Init(network_state_handler_.get(),
108                                  network_profile_handler_.get(),
109                                  network_config_handler_.get(),
110                                  NULL /* network_device_handler */);
111
112    network_connection_handler_.reset(new NetworkConnectionHandler);
113    network_connection_handler_->Init(network_state_handler_.get(),
114                                      network_config_handler_.get(),
115                                      managed_config_handler_.get());
116
117    base::RunLoop().RunUntilIdle();
118  }
119
120  virtual void TearDown() OVERRIDE {
121    managed_config_handler_.reset();
122    network_profile_handler_.reset();
123    network_connection_handler_.reset();
124    network_config_handler_.reset();
125    network_state_handler_.reset();
126    CertLoader::Shutdown();
127    TPMTokenLoader::Shutdown();
128    LoginState::Shutdown();
129    DBusThreadManager::Shutdown();
130  }
131
132 protected:
133  bool Configure(const std::string& json_string) {
134    scoped_ptr<base::DictionaryValue> json_dict =
135        onc::ReadDictionaryFromJson(json_string);
136    if (!json_dict) {
137      LOG(ERROR) << "Error parsing json: " << json_string;
138      return false;
139    }
140    DBusThreadManager::Get()->GetShillManagerClient()->ConfigureService(
141        *json_dict,
142        base::Bind(&ConfigureCallback),
143        base::Bind(&ConfigureErrorCallback));
144    base::RunLoop().RunUntilIdle();
145    return true;
146  }
147
148  void Connect(const std::string& service_path) {
149    const bool check_error_state = true;
150    network_connection_handler_->ConnectToNetwork(
151        service_path,
152        base::Bind(&NetworkConnectionHandlerTest::SuccessCallback,
153                   base::Unretained(this)),
154        base::Bind(&NetworkConnectionHandlerTest::ErrorCallback,
155                   base::Unretained(this)),
156        check_error_state);
157    base::RunLoop().RunUntilIdle();
158  }
159
160  void Disconnect(const std::string& service_path) {
161    network_connection_handler_->DisconnectNetwork(
162        service_path,
163        base::Bind(&NetworkConnectionHandlerTest::SuccessCallback,
164                   base::Unretained(this)),
165        base::Bind(&NetworkConnectionHandlerTest::ErrorCallback,
166                   base::Unretained(this)));
167    base::RunLoop().RunUntilIdle();
168  }
169
170  void SuccessCallback() {
171    result_ = kSuccessResult;
172  }
173
174  void ErrorCallback(const std::string& error_name,
175                     scoped_ptr<base::DictionaryValue> error_data) {
176    result_ = error_name;
177  }
178
179  std::string GetResultAndReset() {
180    std::string result;
181    result.swap(result_);
182    return result;
183  }
184
185  std::string GetServiceStringProperty(const std::string& service_path,
186                                       const std::string& key) {
187    std::string result;
188    const base::DictionaryValue* properties =
189        test_service_client_->GetServiceProperties(service_path);
190    if (properties)
191      properties->GetStringWithoutPathExpansion(key, &result);
192    return result;
193  }
194
195  void StartCertLoader() {
196    CertLoader::Get()->StartWithNSSDB(test_nssdb_.get());
197    base::RunLoop().RunUntilIdle();
198  }
199
200  void LoginToRegularUser() {
201    LoginState::Get()->SetLoggedInState(LoginState::LOGGED_IN_ACTIVE,
202                                        LoginState::LOGGED_IN_USER_REGULAR);
203    base::RunLoop().RunUntilIdle();
204  }
205
206  void ImportClientCertAndKey(const std::string& pkcs12_file,
207                              net::NSSCertDatabase* nssdb,
208                              net::CertificateList* loaded_certs) {
209    std::string pkcs12_data;
210    base::FilePath pkcs12_path =
211        net::GetTestCertsDirectory().Append(pkcs12_file);
212    ASSERT_TRUE(base::ReadFileToString(pkcs12_path, &pkcs12_data));
213
214    scoped_refptr<net::CryptoModule> module(
215        net::CryptoModule::CreateFromHandle(nssdb->GetPrivateSlot().get()));
216    ASSERT_EQ(
217        net::OK,
218        nssdb->ImportFromPKCS12(module, pkcs12_data, base::string16(), false,
219                                loaded_certs));
220    ASSERT_EQ(1U, loaded_certs->size());
221  }
222
223  void SetupPolicy() {
224    const char* kNetworkConfigs =
225        "[ { \"GUID\": \"wifi1\","
226        "    \"Name\": \"wifi1\","
227        "    \"Type\": \"WiFi\","
228        "    \"WiFi\": {"
229        "      \"Security\": \"WPA-PSK\","
230        "      \"SSID\": \"wifi1\","
231        "      \"Passphrase\": \"passphrase\""
232        "    }"
233        "} ]";
234
235    std::string error;
236    scoped_ptr<base::Value> network_configs_value(
237        base::JSONReader::ReadAndReturnError(
238            kNetworkConfigs, base::JSON_ALLOW_TRAILING_COMMAS, NULL, &error));
239    ASSERT_TRUE(network_configs_value) << error;
240
241    base::ListValue* network_configs = NULL;
242    ASSERT_TRUE(network_configs_value->GetAsList(&network_configs));
243
244    base::DictionaryValue global_config;
245    global_config.SetBooleanWithoutPathExpansion(
246        ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
247        true);
248
249    managed_config_handler_->SetPolicy(::onc::ONC_SOURCE_USER_POLICY,
250                                       "", // userhash
251                                       *network_configs,
252                                       global_config);
253    base::RunLoop().RunUntilIdle();
254  }
255
256  scoped_ptr<NetworkStateHandler> network_state_handler_;
257  scoped_ptr<NetworkConfigurationHandler> network_config_handler_;
258  scoped_ptr<NetworkConnectionHandler> network_connection_handler_;
259  scoped_ptr<ManagedNetworkConfigurationHandlerImpl> managed_config_handler_;
260  scoped_ptr<NetworkProfileHandler> network_profile_handler_;
261  crypto::ScopedTestNSSChromeOSUser user_;
262  ShillManagerClient::TestInterface* test_manager_client_;
263  ShillServiceClient::TestInterface* test_service_client_;
264  scoped_ptr<net::NSSCertDatabaseChromeOS> test_nssdb_;
265  base::MessageLoopForUI message_loop_;
266  std::string result_;
267
268 private:
269  DISALLOW_COPY_AND_ASSIGN(NetworkConnectionHandlerTest);
270};
271
272namespace {
273
274const char* kConfigConnectable =
275    "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"idle\", "
276    "  \"Connectable\": true }";
277const char* kConfigConnected =
278    "{ \"GUID\": \"wifi1\", \"Type\": \"wifi\", \"State\": \"online\" }";
279const char* kConfigConnecting =
280    "{ \"GUID\": \"wifi2\", \"Type\": \"wifi\", \"State\": \"association\" }";
281const char* kConfigRequiresPassphrase =
282    "{ \"GUID\": \"wifi3\", \"Type\": \"wifi\", "
283    "  \"PassphraseRequired\": true }";
284const char* kConfigRequiresActivation =
285    "{ \"GUID\": \"cellular1\", \"Type\": \"cellular\","
286    "  \"Cellular.ActivationState\": \"not-activated\" }";
287
288}  // namespace
289
290TEST_F(NetworkConnectionHandlerTest, NetworkConnectionHandlerConnectSuccess) {
291  EXPECT_TRUE(Configure(kConfigConnectable));
292  Connect("wifi0");
293  EXPECT_EQ(kSuccessResult, GetResultAndReset());
294  EXPECT_EQ(shill::kStateOnline,
295            GetServiceStringProperty("wifi0", shill::kStateProperty));
296}
297
298// Handles basic failure cases.
299TEST_F(NetworkConnectionHandlerTest, NetworkConnectionHandlerConnectFailure) {
300  Connect("no-network");
301  EXPECT_EQ(NetworkConnectionHandler::kErrorConfigureFailed,
302            GetResultAndReset());
303
304  EXPECT_TRUE(Configure(kConfigConnected));
305  Connect("wifi1");
306  EXPECT_EQ(NetworkConnectionHandler::kErrorConnected, GetResultAndReset());
307
308  EXPECT_TRUE(Configure(kConfigConnecting));
309  Connect("wifi2");
310  EXPECT_EQ(NetworkConnectionHandler::kErrorConnecting, GetResultAndReset());
311
312  EXPECT_TRUE(Configure(kConfigRequiresPassphrase));
313  Connect("wifi3");
314  EXPECT_EQ(NetworkConnectionHandler::kErrorPassphraseRequired,
315            GetResultAndReset());
316
317  EXPECT_TRUE(Configure(kConfigRequiresActivation));
318  Connect("cellular1");
319  EXPECT_EQ(NetworkConnectionHandler::kErrorActivationRequired,
320            GetResultAndReset());
321}
322
323namespace {
324
325const char* kConfigRequiresCertificateTemplate =
326    "{ \"GUID\": \"wifi4\", \"Type\": \"wifi\", \"Connectable\": false,"
327    "  \"Security\": \"802_1x\","
328    "  \"UIData\": \"{"
329    "    \\\"certificate_type\\\": \\\"pattern\\\","
330    "    \\\"certificate_pattern\\\": {"
331    "      \\\"Subject\\\": {\\\"CommonName\\\": \\\"%s\\\" }"
332    "   } }\" }";
333
334}  // namespace
335
336// Handle certificates.
337TEST_F(NetworkConnectionHandlerTest, ConnectCertificateMissing) {
338  StartCertLoader();
339
340  EXPECT_TRUE(Configure(
341      base::StringPrintf(kConfigRequiresCertificateTemplate, "unknown")));
342  Connect("wifi4");
343  EXPECT_EQ(NetworkConnectionHandler::kErrorCertificateRequired,
344            GetResultAndReset());
345}
346
347TEST_F(NetworkConnectionHandlerTest, ConnectWithCertificateSuccess) {
348  StartCertLoader();
349
350  net::CertificateList certs;
351  ImportClientCertAndKey("websocket_client_cert.p12",
352                         test_nssdb_.get(),
353                         &certs);
354
355  EXPECT_TRUE(Configure(
356      base::StringPrintf(kConfigRequiresCertificateTemplate,
357                         certs[0]->subject().common_name.c_str())));
358
359  Connect("wifi4");
360  EXPECT_EQ(kSuccessResult, GetResultAndReset());
361}
362
363TEST_F(NetworkConnectionHandlerTest,
364       ConnectWithCertificateRequestedBeforeCertsAreLoaded) {
365  net::CertificateList certs;
366  ImportClientCertAndKey("websocket_client_cert.p12",
367                         test_nssdb_.get(),
368                         &certs);
369
370  EXPECT_TRUE(Configure(
371      base::StringPrintf(kConfigRequiresCertificateTemplate,
372                         certs[0]->subject().common_name.c_str())));
373
374  Connect("wifi4");
375
376  // Connect request came before the cert loader loaded certificates, so the
377  // connect request should have been throttled until the certificates are
378  // loaded.
379  EXPECT_EQ("", GetResultAndReset());
380
381  StartCertLoader();
382
383  // |StartCertLoader| should have triggered certificate loading.
384  // When the certificates got loaded, the connection request should have
385  // proceeded and eventually succeeded.
386  EXPECT_EQ(kSuccessResult, GetResultAndReset());
387}
388
389TEST_F(NetworkConnectionHandlerTest,
390       NetworkConnectionHandlerDisconnectSuccess) {
391  EXPECT_TRUE(Configure(kConfigConnected));
392  Disconnect("wifi1");
393  EXPECT_EQ(kSuccessResult, GetResultAndReset());
394}
395
396TEST_F(NetworkConnectionHandlerTest,
397       NetworkConnectionHandlerDisconnectFailure) {
398  Connect("no-network");
399  EXPECT_EQ(NetworkConnectionHandler::kErrorConfigureFailed,
400            GetResultAndReset());
401
402  EXPECT_TRUE(Configure(kConfigConnectable));
403  Disconnect("wifi0");
404  EXPECT_EQ(NetworkConnectionHandler::kErrorNotConnected, GetResultAndReset());
405}
406
407namespace {
408
409const char* kConfigUnmanagedSharedConnected =
410    "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"online\" }";
411const char* kConfigManagedSharedConnectable =
412    "{ \"GUID\": \"wifi1\", \"Type\": \"wifi\", \"State\": \"idle\", "
413    "  \"Connectable\": true }";
414
415}  // namespace
416
417TEST_F(NetworkConnectionHandlerTest, ReconnectOnLoginEarlyPolicyLoading) {
418  EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected));
419  EXPECT_TRUE(Configure(kConfigManagedSharedConnectable));
420  test_manager_client_->SetBestServiceToConnect("wifi1");
421
422  // User login shouldn't trigger any change because policy is not loaded yet.
423  LoginToRegularUser();
424  EXPECT_EQ(shill::kStateOnline,
425            GetServiceStringProperty("wifi0", shill::kStateProperty));
426  EXPECT_EQ(shill::kStateIdle,
427            GetServiceStringProperty("wifi1", shill::kStateProperty));
428
429  // Policy application should disconnect from the shared and unmanaged network.
430  SetupPolicy();
431  EXPECT_EQ(shill::kStateIdle,
432            GetServiceStringProperty("wifi0", shill::kStateProperty));
433  EXPECT_EQ(shill::kStateIdle,
434            GetServiceStringProperty("wifi1", shill::kStateProperty));
435
436  // Certificate loading should trigger connecting to the 'best' network.
437  StartCertLoader();
438  EXPECT_EQ(shill::kStateIdle,
439            GetServiceStringProperty("wifi0", shill::kStateProperty));
440  EXPECT_EQ(shill::kStateOnline,
441            GetServiceStringProperty("wifi1", shill::kStateProperty));
442}
443
444TEST_F(NetworkConnectionHandlerTest, ReconnectOnLoginLatePolicyLoading) {
445  EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected));
446  EXPECT_TRUE(Configure(kConfigManagedSharedConnectable));
447  test_manager_client_->SetBestServiceToConnect("wifi1");
448
449  // User login and certificate loading shouldn't trigger any change until the
450  // policy is loaded.
451  LoginToRegularUser();
452  StartCertLoader();
453  EXPECT_EQ(shill::kStateOnline,
454            GetServiceStringProperty("wifi0", shill::kStateProperty));
455  EXPECT_EQ(shill::kStateIdle,
456            GetServiceStringProperty("wifi1", shill::kStateProperty));
457
458  SetupPolicy();
459  EXPECT_EQ(shill::kStateIdle,
460            GetServiceStringProperty("wifi0", shill::kStateProperty));
461  EXPECT_EQ(shill::kStateOnline,
462            GetServiceStringProperty("wifi1", shill::kStateProperty));
463}
464
465}  // namespace chromeos
466