network_connection_handler_unittest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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/files/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/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_internal.h"
29#include "crypto/scoped_test_nss_chromeos_user.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    DBusThreadManager::Initialize();
81    DBusThreadManager* dbus_manager = DBusThreadManager::Get();
82    test_manager_client_ =
83        dbus_manager->GetShillManagerClient()->GetTestInterface();
84    test_service_client_ =
85        dbus_manager->GetShillServiceClient()->GetTestInterface();
86
87    test_manager_client_->AddTechnology(shill::kTypeWifi, true /* enabled */);
88    dbus_manager->GetShillDeviceClient()->GetTestInterface()->AddDevice(
89        "/device/wifi1", shill::kTypeWifi, "wifi_device1");
90    test_manager_client_->AddTechnology(shill::kTypeCellular,
91                                        true /* enabled */);
92    dbus_manager->GetShillProfileClient()->GetTestInterface()->AddProfile(
93        "shared_profile_path", std::string() /* shared profile */);
94    dbus_manager->GetShillProfileClient()->GetTestInterface()->AddProfile(
95        "user_profile_path", user_.username_hash());
96
97    base::RunLoop().RunUntilIdle();
98    LoginState::Initialize();
99    network_state_handler_.reset(NetworkStateHandler::InitializeForTest());
100    network_config_handler_.reset(
101        NetworkConfigurationHandler::InitializeForTest(
102            network_state_handler_.get()));
103
104    network_profile_handler_.reset(new NetworkProfileHandler());
105    network_profile_handler_->Init();
106
107    managed_config_handler_.reset(new ManagedNetworkConfigurationHandlerImpl());
108    managed_config_handler_->Init(network_state_handler_.get(),
109                                  network_profile_handler_.get(),
110                                  network_config_handler_.get(),
111                                  NULL /* network_device_handler */);
112
113    network_connection_handler_.reset(new NetworkConnectionHandler);
114    network_connection_handler_->Init(network_state_handler_.get(),
115                                      network_config_handler_.get(),
116                                      managed_config_handler_.get());
117
118    base::RunLoop().RunUntilIdle();
119  }
120
121  virtual void TearDown() OVERRIDE {
122    managed_config_handler_.reset();
123    network_profile_handler_.reset();
124    network_connection_handler_.reset();
125    network_config_handler_.reset();
126    network_state_handler_.reset();
127    CertLoader::Shutdown();
128    TPMTokenLoader::Shutdown();
129    LoginState::Shutdown();
130    DBusThreadManager::Shutdown();
131  }
132
133 protected:
134  bool Configure(const std::string& json_string) {
135    scoped_ptr<base::DictionaryValue> json_dict =
136        onc::ReadDictionaryFromJson(json_string);
137    if (!json_dict) {
138      LOG(ERROR) << "Error parsing json: " << json_string;
139      return false;
140    }
141    DBusThreadManager::Get()->GetShillManagerClient()->ConfigureService(
142        *json_dict,
143        base::Bind(&ConfigureCallback),
144        base::Bind(&ConfigureErrorCallback));
145    base::RunLoop().RunUntilIdle();
146    return true;
147  }
148
149  void Connect(const std::string& service_path) {
150    const bool check_error_state = true;
151    network_connection_handler_->ConnectToNetwork(
152        service_path,
153        base::Bind(&NetworkConnectionHandlerTest::SuccessCallback,
154                   base::Unretained(this)),
155        base::Bind(&NetworkConnectionHandlerTest::ErrorCallback,
156                   base::Unretained(this)),
157        check_error_state);
158    base::RunLoop().RunUntilIdle();
159  }
160
161  void Disconnect(const std::string& service_path) {
162    network_connection_handler_->DisconnectNetwork(
163        service_path,
164        base::Bind(&NetworkConnectionHandlerTest::SuccessCallback,
165                   base::Unretained(this)),
166        base::Bind(&NetworkConnectionHandlerTest::ErrorCallback,
167                   base::Unretained(this)));
168    base::RunLoop().RunUntilIdle();
169  }
170
171  void SuccessCallback() {
172    result_ = kSuccessResult;
173  }
174
175  void ErrorCallback(const std::string& error_name,
176                     scoped_ptr<base::DictionaryValue> error_data) {
177    result_ = error_name;
178  }
179
180  std::string GetResultAndReset() {
181    std::string result;
182    result.swap(result_);
183    return result;
184  }
185
186  std::string GetServiceStringProperty(const std::string& service_path,
187                                       const std::string& key) {
188    std::string result;
189    const base::DictionaryValue* properties =
190        test_service_client_->GetServiceProperties(service_path);
191    if (properties)
192      properties->GetStringWithoutPathExpansion(key, &result);
193    return result;
194  }
195
196  void StartCertLoader() {
197    CertLoader::Get()->StartWithNSSDB(test_nssdb_.get());
198    base::RunLoop().RunUntilIdle();
199  }
200
201  void LoginToRegularUser() {
202    LoginState::Get()->SetLoggedInState(LoginState::LOGGED_IN_ACTIVE,
203                                        LoginState::LOGGED_IN_USER_REGULAR);
204    base::RunLoop().RunUntilIdle();
205  }
206
207  scoped_refptr<net::X509Certificate> ImportTestClientCert() {
208    net::CertificateList ca_cert_list =
209        net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
210                                           "websocket_cacert.pem",
211                                           net::X509Certificate::FORMAT_AUTO);
212    if (ca_cert_list.empty()) {
213      LOG(ERROR) << "No CA cert loaded.";
214      return NULL;
215    }
216    net::NSSCertDatabase::ImportCertFailureList failures;
217    EXPECT_TRUE(test_nssdb_->ImportCACerts(
218        ca_cert_list, net::NSSCertDatabase::TRUST_DEFAULT, &failures));
219    if (!failures.empty()) {
220      LOG(ERROR) << net::ErrorToString(failures[0].net_error);
221      return NULL;
222    }
223
224    std::string pkcs12_data;
225    base::FilePath pkcs12_path =
226        net::GetTestCertsDirectory().Append("websocket_client_cert.p12");
227    if (!base::ReadFileToString(pkcs12_path, &pkcs12_data))
228      return NULL;
229
230    net::CertificateList loaded_certs;
231    scoped_refptr<net::CryptoModule> module(net::CryptoModule::CreateFromHandle(
232        test_nssdb_->GetPrivateSlot().get()));
233    if (test_nssdb_->ImportFromPKCS12(module.get(),
234                                      pkcs12_data,
235                                      base::string16(),
236                                      false,
237                                      &loaded_certs) != net::OK) {
238      LOG(ERROR) << "Error while importing to NSSDB.";
239      return NULL;
240    }
241
242    // File contains two certs, the client cert first and the CA cert second.
243    if (loaded_certs.size() != 2U) {
244      LOG(ERROR) << "Expected two certs in file, found " << loaded_certs.size();
245      return NULL;
246    }
247    return loaded_certs[0];
248  }
249
250  void SetupPolicy(const std::string& network_configs_json,
251                   const base::DictionaryValue& global_config,
252                   bool user_policy) {
253    std::string error;
254    scoped_ptr<base::Value> network_configs_value(
255        base::JSONReader::ReadAndReturnError(network_configs_json,
256                                             base::JSON_ALLOW_TRAILING_COMMAS,
257                                             NULL,
258                                             &error));
259    ASSERT_TRUE(network_configs_value) << error;
260
261    base::ListValue* network_configs = NULL;
262    ASSERT_TRUE(network_configs_value->GetAsList(&network_configs));
263
264    if (user_policy) {
265      managed_config_handler_->SetPolicy(
266          ::onc::ONC_SOURCE_USER_POLICY,
267          user_.username_hash(),
268          *network_configs,
269          global_config);
270    } else {
271      managed_config_handler_->SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY,
272                                         std::string(),  // no username hash
273                                         *network_configs,
274                                         global_config);
275    }
276    base::RunLoop().RunUntilIdle();
277  }
278
279  scoped_ptr<NetworkStateHandler> network_state_handler_;
280  scoped_ptr<NetworkConfigurationHandler> network_config_handler_;
281  scoped_ptr<NetworkConnectionHandler> network_connection_handler_;
282  scoped_ptr<ManagedNetworkConfigurationHandlerImpl> managed_config_handler_;
283  scoped_ptr<NetworkProfileHandler> network_profile_handler_;
284  crypto::ScopedTestNSSChromeOSUser user_;
285  ShillManagerClient::TestInterface* test_manager_client_;
286  ShillServiceClient::TestInterface* test_service_client_;
287  scoped_ptr<net::NSSCertDatabaseChromeOS> test_nssdb_;
288  base::MessageLoopForUI message_loop_;
289  std::string result_;
290
291 private:
292  DISALLOW_COPY_AND_ASSIGN(NetworkConnectionHandlerTest);
293};
294
295namespace {
296
297const char* kConfigConnectable =
298    "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"idle\", "
299    "  \"Connectable\": true }";
300const char* kConfigConnected =
301    "{ \"GUID\": \"wifi1\", \"Type\": \"wifi\", \"State\": \"online\" }";
302const char* kConfigConnecting =
303    "{ \"GUID\": \"wifi2\", \"Type\": \"wifi\", \"State\": \"association\" }";
304const char* kConfigRequiresPassphrase =
305    "{ \"GUID\": \"wifi3\", \"Type\": \"wifi\", "
306    "  \"PassphraseRequired\": true }";
307
308}  // namespace
309
310TEST_F(NetworkConnectionHandlerTest, NetworkConnectionHandlerConnectSuccess) {
311  EXPECT_TRUE(Configure(kConfigConnectable));
312  Connect("wifi0");
313  EXPECT_EQ(kSuccessResult, GetResultAndReset());
314  EXPECT_EQ(shill::kStateOnline,
315            GetServiceStringProperty("wifi0", shill::kStateProperty));
316}
317
318// Handles basic failure cases.
319TEST_F(NetworkConnectionHandlerTest, NetworkConnectionHandlerConnectFailure) {
320  Connect("no-network");
321  EXPECT_EQ(NetworkConnectionHandler::kErrorConfigureFailed,
322            GetResultAndReset());
323
324  EXPECT_TRUE(Configure(kConfigConnected));
325  Connect("wifi1");
326  EXPECT_EQ(NetworkConnectionHandler::kErrorConnected, GetResultAndReset());
327
328  EXPECT_TRUE(Configure(kConfigConnecting));
329  Connect("wifi2");
330  EXPECT_EQ(NetworkConnectionHandler::kErrorConnecting, GetResultAndReset());
331
332  EXPECT_TRUE(Configure(kConfigRequiresPassphrase));
333  Connect("wifi3");
334  EXPECT_EQ(NetworkConnectionHandler::kErrorPassphraseRequired,
335            GetResultAndReset());
336}
337
338namespace {
339
340const char* kPolicyWithCertPatternTemplate =
341    "[ { \"GUID\": \"wifi4\","
342    "    \"Name\": \"wifi4\","
343    "    \"Type\": \"WiFi\","
344    "    \"WiFi\": {"
345    "      \"Security\": \"WPA-EAP\","
346    "      \"SSID\": \"wifi_ssid\","
347    "      \"EAP\": {"
348    "        \"Outer\": \"EAP-TLS\","
349    "        \"ClientCertType\": \"Pattern\","
350    "        \"ClientCertPattern\": {"
351    "          \"Subject\": {"
352    "            \"CommonName\" : \"%s\""
353    "          }"
354    "        }"
355    "      }"
356    "    }"
357    "} ]";
358
359}  // namespace
360
361// Handle certificates.
362TEST_F(NetworkConnectionHandlerTest, ConnectCertificateMissing) {
363  StartCertLoader();
364  SetupPolicy(base::StringPrintf(kPolicyWithCertPatternTemplate, "unknown"),
365              base::DictionaryValue(),  // no global config
366              true);                    // load as user policy
367
368  Connect("wifi4");
369  EXPECT_EQ(NetworkConnectionHandler::kErrorCertificateRequired,
370            GetResultAndReset());
371}
372
373TEST_F(NetworkConnectionHandlerTest, ConnectWithCertificateSuccess) {
374  StartCertLoader();
375  scoped_refptr<net::X509Certificate> cert = ImportTestClientCert();
376  ASSERT_TRUE(cert.get());
377
378  SetupPolicy(base::StringPrintf(kPolicyWithCertPatternTemplate,
379                                 cert->subject().common_name.c_str()),
380              base::DictionaryValue(),  // no global config
381              true);                    // load as user policy
382
383  Connect("wifi4");
384  EXPECT_EQ(kSuccessResult, GetResultAndReset());
385}
386
387// Disabled, see http://crbug.com/396729.
388TEST_F(NetworkConnectionHandlerTest,
389       DISABLED_ConnectWithCertificateRequestedBeforeCertsAreLoaded) {
390  scoped_refptr<net::X509Certificate> cert = ImportTestClientCert();
391  ASSERT_TRUE(cert.get());
392
393  SetupPolicy(base::StringPrintf(kPolicyWithCertPatternTemplate,
394                                 cert->subject().common_name.c_str()),
395              base::DictionaryValue(),  // no global config
396              true);                    // load as user policy
397
398  Connect("wifi4");
399
400  // Connect request came before the cert loader loaded certificates, so the
401  // connect request should have been throttled until the certificates are
402  // loaded.
403  EXPECT_EQ("", GetResultAndReset());
404
405  StartCertLoader();
406
407  // |StartCertLoader| should have triggered certificate loading.
408  // When the certificates got loaded, the connection request should have
409  // proceeded and eventually succeeded.
410  EXPECT_EQ(kSuccessResult, GetResultAndReset());
411}
412
413TEST_F(NetworkConnectionHandlerTest,
414       NetworkConnectionHandlerDisconnectSuccess) {
415  EXPECT_TRUE(Configure(kConfigConnected));
416  Disconnect("wifi1");
417  EXPECT_EQ(kSuccessResult, GetResultAndReset());
418}
419
420TEST_F(NetworkConnectionHandlerTest,
421       NetworkConnectionHandlerDisconnectFailure) {
422  Connect("no-network");
423  EXPECT_EQ(NetworkConnectionHandler::kErrorConfigureFailed,
424            GetResultAndReset());
425
426  EXPECT_TRUE(Configure(kConfigConnectable));
427  Disconnect("wifi0");
428  EXPECT_EQ(NetworkConnectionHandler::kErrorNotConnected, GetResultAndReset());
429}
430
431namespace {
432
433const char* kConfigUnmanagedSharedConnected =
434    "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"online\" }";
435const char* kConfigManagedSharedConnectable =
436    "{ \"GUID\": \"wifi1\", \"Type\": \"wifi\", \"State\": \"idle\", "
437    "  \"Connectable\": true }";
438
439const char* kPolicy =
440    "[ { \"GUID\": \"wifi1\","
441    "    \"Name\": \"wifi1\","
442    "    \"Type\": \"WiFi\","
443    "    \"WiFi\": {"
444    "      \"Security\": \"WPA-PSK\","
445    "      \"SSID\": \"wifi1\","
446    "      \"Passphrase\": \"passphrase\""
447    "    }"
448    "} ]";
449
450}  // namespace
451
452TEST_F(NetworkConnectionHandlerTest, ReconnectOnLoginEarlyPolicyLoading) {
453  EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected));
454  EXPECT_TRUE(Configure(kConfigManagedSharedConnectable));
455  test_manager_client_->SetBestServiceToConnect("wifi1");
456
457  // User login shouldn't trigger any change because policy is not loaded yet.
458  LoginToRegularUser();
459  EXPECT_EQ(shill::kStateOnline,
460            GetServiceStringProperty("wifi0", shill::kStateProperty));
461  EXPECT_EQ(shill::kStateIdle,
462            GetServiceStringProperty("wifi1", shill::kStateProperty));
463
464  // Applying the policy which restricts autoconnect should disconnect from the
465  // shared, unmanaged network.
466  base::DictionaryValue global_config;
467  global_config.SetBooleanWithoutPathExpansion(
468      ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
469      true);
470
471  SetupPolicy(kPolicy, global_config, false /* load as device policy */);
472  EXPECT_EQ(shill::kStateIdle,
473            GetServiceStringProperty("wifi0", shill::kStateProperty));
474  EXPECT_EQ(shill::kStateIdle,
475            GetServiceStringProperty("wifi1", shill::kStateProperty));
476
477  // Certificate loading should trigger connecting to the 'best' network.
478  StartCertLoader();
479  EXPECT_EQ(shill::kStateIdle,
480            GetServiceStringProperty("wifi0", shill::kStateProperty));
481  EXPECT_EQ(shill::kStateOnline,
482            GetServiceStringProperty("wifi1", shill::kStateProperty));
483}
484
485TEST_F(NetworkConnectionHandlerTest, ReconnectOnLoginLatePolicyLoading) {
486  EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected));
487  EXPECT_TRUE(Configure(kConfigManagedSharedConnectable));
488  test_manager_client_->SetBestServiceToConnect("wifi1");
489
490  // User login and certificate loading shouldn't trigger any change until the
491  // policy is loaded.
492  LoginToRegularUser();
493  StartCertLoader();
494  EXPECT_EQ(shill::kStateOnline,
495            GetServiceStringProperty("wifi0", shill::kStateProperty));
496  EXPECT_EQ(shill::kStateIdle,
497            GetServiceStringProperty("wifi1", shill::kStateProperty));
498
499  // Applying the policy which restricts autoconnect should disconnect from the
500  // shared, unmanaged network.
501  base::DictionaryValue global_config;
502  global_config.SetBooleanWithoutPathExpansion(
503      ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
504      true);
505
506  SetupPolicy(kPolicy, global_config, false /* load as device policy */);
507  EXPECT_EQ(shill::kStateIdle,
508            GetServiceStringProperty("wifi0", shill::kStateProperty));
509  EXPECT_EQ(shill::kStateOnline,
510            GetServiceStringProperty("wifi1", shill::kStateProperty));
511}
512
513}  // namespace chromeos
514