Home | History | Annotate | Download | only in network
      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 
     38 namespace {
     39 
     40 const char* kSuccessResult = "success";
     41 
     42 void ConfigureCallback(const dbus::ObjectPath& result) {
     43 }
     44 
     45 void ConfigureErrorCallback(const std::string& error_name,
     46                             const std::string& error_message) {
     47 }
     48 
     49 }  // namespace
     50 
     51 namespace chromeos {
     52 
     53 class 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 
    295 namespace {
    296 
    297 const char* kConfigConnectable =
    298     "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"idle\", "
    299     "  \"Connectable\": true }";
    300 const char* kConfigConnected =
    301     "{ \"GUID\": \"wifi1\", \"Type\": \"wifi\", \"State\": \"online\" }";
    302 const char* kConfigConnecting =
    303     "{ \"GUID\": \"wifi2\", \"Type\": \"wifi\", \"State\": \"association\" }";
    304 const char* kConfigRequiresPassphrase =
    305     "{ \"GUID\": \"wifi3\", \"Type\": \"wifi\", "
    306     "  \"PassphraseRequired\": true }";
    307 
    308 }  // namespace
    309 
    310 TEST_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.
    319 TEST_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 
    338 namespace {
    339 
    340 const 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.
    362 TEST_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 
    373 TEST_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.
    388 TEST_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 
    413 TEST_F(NetworkConnectionHandlerTest,
    414        NetworkConnectionHandlerDisconnectSuccess) {
    415   EXPECT_TRUE(Configure(kConfigConnected));
    416   Disconnect("wifi1");
    417   EXPECT_EQ(kSuccessResult, GetResultAndReset());
    418 }
    419 
    420 TEST_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 
    431 namespace {
    432 
    433 const char* kConfigUnmanagedSharedConnected =
    434     "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"online\" }";
    435 const char* kConfigManagedSharedConnectable =
    436     "{ \"GUID\": \"wifi1\", \"Type\": \"wifi\", \"State\": \"idle\", "
    437     "  \"Connectable\": true }";
    438 
    439 const 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 
    452 TEST_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 
    485 TEST_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