1 // Copyright 2014 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.
5 #include "chromeos/network/auto_connect_handler.h"
10 #include "base/callback.h"
11 #include "base/files/file_util.h"
12 #include "base/json/json_reader.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/run_loop.h"
16 #include "base/strings/stringprintf.h"
17 #include "chromeos/cert_loader.h"
18 #include "chromeos/dbus/dbus_thread_manager.h"
19 #include "chromeos/dbus/shill_device_client.h"
20 #include "chromeos/dbus/shill_manager_client.h"
21 #include "chromeos/dbus/shill_profile_client.h"
22 #include "chromeos/dbus/shill_service_client.h"
23 #include "chromeos/network/client_cert_resolver.h"
24 #include "chromeos/network/managed_network_configuration_handler_impl.h"
25 #include "chromeos/network/network_configuration_handler.h"
26 #include "chromeos/network/network_profile_handler.h"
27 #include "chromeos/network/network_state_handler.h"
28 #include "chromeos/network/onc/onc_utils.h"
29 #include "components/onc/onc_constants.h"
30 #include "crypto/scoped_nss_types.h"
31 #include "crypto/scoped_test_nss_db.h"
32 #include "net/base/net_errors.h"
33 #include "net/base/test_data_directory.h"
34 #include "net/cert/nss_cert_database_chromeos.h"
35 #include "net/cert/x509_certificate.h"
36 #include "net/test/cert_test_util.h"
37 #include "testing/gtest/include/gtest/gtest.h"
38 #include "third_party/cros_system_api/dbus/service_constants.h"
44 const char* kUserHash
= "user_hash";
46 void ConfigureCallback(const dbus::ObjectPath
& result
) {
49 void FailErrorCallback(const std::string
& error_name
,
50 const std::string
& error_message
) {
51 // This function is not expected to be called.
55 class TestCertResolveObserver
: public ClientCertResolver::Observer
{
57 explicit TestCertResolveObserver(ClientCertResolver
* cert_resolver
)
58 : changed_network_properties_(false), cert_resolver_(cert_resolver
) {
59 cert_resolver_
->AddObserver(this);
62 void ResolveRequestCompleted(bool changed_network_properties
) override
{
63 cert_resolver_
->RemoveObserver(this);
64 changed_network_properties_
= changed_network_properties
;
67 bool DidNetworkPropertiesChange() { return changed_network_properties_
; }
70 bool changed_network_properties_
;
71 ClientCertResolver
* cert_resolver_
;
76 class AutoConnectHandlerTest
: public testing::Test
{
78 AutoConnectHandlerTest()
79 : test_manager_client_(nullptr), test_service_client_(nullptr) {}
81 void SetUp() override
{
82 ASSERT_TRUE(test_nssdb_
.is_open());
84 // Use the same DB for public and private slot.
85 test_nsscertdb_
.reset(new net::NSSCertDatabaseChromeOS(
86 crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_
.slot())),
87 crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_
.slot()))));
88 test_nsscertdb_
->SetSlowTaskRunnerForTest(message_loop_
.task_runner());
90 CertLoader::Initialize();
91 CertLoader::ForceHardwareBackedForTesting();
93 DBusThreadManager::Initialize();
94 DBusThreadManager
* dbus_manager
= DBusThreadManager::Get();
95 test_manager_client_
=
96 dbus_manager
->GetShillManagerClient()->GetTestInterface();
97 test_service_client_
=
98 dbus_manager
->GetShillServiceClient()->GetTestInterface();
100 test_manager_client_
->AddTechnology(shill::kTypeWifi
, true /* enabled */);
101 dbus_manager
->GetShillDeviceClient()->GetTestInterface()->AddDevice(
102 "/device/wifi1", shill::kTypeWifi
, "wifi_device1");
103 test_manager_client_
->AddTechnology(shill::kTypeCellular
,
105 dbus_manager
->GetShillProfileClient()->GetTestInterface()->AddProfile(
106 "shared_profile_path", std::string() /* shared profile */);
107 dbus_manager
->GetShillProfileClient()->GetTestInterface()->AddProfile(
108 "user_profile_path", kUserHash
);
110 base::RunLoop().RunUntilIdle();
111 LoginState::Initialize();
112 network_state_handler_
.reset(NetworkStateHandler::InitializeForTest());
113 network_config_handler_
.reset(
114 NetworkConfigurationHandler::InitializeForTest(
115 network_state_handler_
.get(), NULL
/* network_device_handler */));
117 network_profile_handler_
.reset(new NetworkProfileHandler());
118 network_profile_handler_
->Init();
120 managed_config_handler_
.reset(new ManagedNetworkConfigurationHandlerImpl());
121 managed_config_handler_
->Init(
122 network_state_handler_
.get(), network_profile_handler_
.get(),
123 network_config_handler_
.get(), nullptr /* network_device_handler */);
125 client_cert_resolver_
.reset(new ClientCertResolver());
126 client_cert_resolver_
->Init(network_state_handler_
.get(),
127 managed_config_handler_
.get());
128 client_cert_resolver_
->SetSlowTaskRunnerForTest(
129 message_loop_
.task_runner());
131 auto_connect_handler_
.reset(new AutoConnectHandler());
132 auto_connect_handler_
->Init(client_cert_resolver_
.get(),
133 nullptr, // no connection handler
134 network_state_handler_
.get(),
135 managed_config_handler_
.get());
137 base::RunLoop().RunUntilIdle();
140 void TearDown() override
{
141 auto_connect_handler_
.reset();
142 client_cert_resolver_
.reset();
143 managed_config_handler_
.reset();
144 network_profile_handler_
.reset();
145 network_config_handler_
.reset();
146 network_state_handler_
.reset();
147 CertLoader::Shutdown();
148 LoginState::Shutdown();
149 DBusThreadManager::Shutdown();
153 bool Configure(const std::string
& json_string
) {
154 scoped_ptr
<base::DictionaryValue
> json_dict
=
155 onc::ReadDictionaryFromJson(json_string
);
157 LOG(ERROR
) << "Error parsing json: " << json_string
;
160 DBusThreadManager::Get()->GetShillManagerClient()->ConfigureService(
161 *json_dict
, base::Bind(&ConfigureCallback
),
162 base::Bind(&FailErrorCallback
));
163 base::RunLoop().RunUntilIdle();
167 std::string
GetServiceState(const std::string
& service_path
) {
168 const base::DictionaryValue
* properties
=
169 test_service_client_
->GetServiceProperties(service_path
);
172 properties
->GetStringWithoutPathExpansion(shill::kStateProperty
, &result
);
176 void StartCertLoader() {
177 CertLoader::Get()->StartWithNSSDB(test_nsscertdb_
.get());
178 base::RunLoop().RunUntilIdle();
181 void LoginToRegularUser() {
182 LoginState::Get()->SetLoggedInState(LoginState::LOGGED_IN_ACTIVE
,
183 LoginState::LOGGED_IN_USER_REGULAR
);
184 base::RunLoop().RunUntilIdle();
187 scoped_refptr
<net::X509Certificate
> ImportTestClientCert() {
188 net::CertificateList ca_cert_list
= net::CreateCertificateListFromFile(
189 net::GetTestCertsDirectory(), "client_1_ca.pem",
190 net::X509Certificate::FORMAT_AUTO
);
191 if (ca_cert_list
.empty()) {
192 LOG(ERROR
) << "No CA cert loaded.";
195 net::NSSCertDatabase::ImportCertFailureList failures
;
196 EXPECT_TRUE(test_nsscertdb_
->ImportCACerts(
197 ca_cert_list
, net::NSSCertDatabase::TRUST_DEFAULT
, &failures
));
198 if (!failures
.empty()) {
199 LOG(ERROR
) << net::ErrorToString(failures
[0].net_error
);
203 // Import a client cert signed by that CA.
204 scoped_refptr
<net::X509Certificate
> client_cert(
205 net::ImportClientCertAndKeyFromFile(net::GetTestCertsDirectory(),
206 "client_1.pem", "client_1.pk8",
207 test_nssdb_
.slot()));
211 void SetupPolicy(const std::string
& network_configs_json
,
212 const base::DictionaryValue
& global_config
,
214 scoped_ptr
<base::ListValue
> network_configs(new base::ListValue
);
215 if (!network_configs_json
.empty()) {
217 base::Value
* network_configs_value
= base::JSONReader::ReadAndReturnError(
218 network_configs_json
, base::JSON_ALLOW_TRAILING_COMMAS
, nullptr,
220 ASSERT_TRUE(network_configs_value
) << error
;
221 base::ListValue
* network_configs_list
= nullptr;
222 ASSERT_TRUE(network_configs_value
->GetAsList(&network_configs_list
));
223 network_configs
.reset(network_configs_list
);
227 managed_config_handler_
->SetPolicy(::onc::ONC_SOURCE_USER_POLICY
,
228 kUserHash
, *network_configs
,
231 managed_config_handler_
->SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY
,
232 std::string(), // no username hash
233 *network_configs
, global_config
);
235 base::RunLoop().RunUntilIdle();
238 scoped_ptr
<AutoConnectHandler
> auto_connect_handler_
;
239 scoped_ptr
<ClientCertResolver
> client_cert_resolver_
;
240 scoped_ptr
<NetworkStateHandler
> network_state_handler_
;
241 scoped_ptr
<NetworkConfigurationHandler
> network_config_handler_
;
242 scoped_ptr
<ManagedNetworkConfigurationHandlerImpl
> managed_config_handler_
;
243 scoped_ptr
<NetworkProfileHandler
> network_profile_handler_
;
244 ShillManagerClient::TestInterface
* test_manager_client_
;
245 ShillServiceClient::TestInterface
* test_service_client_
;
246 crypto::ScopedTestNSSDB test_nssdb_
;
247 scoped_ptr
<net::NSSCertDatabaseChromeOS
> test_nsscertdb_
;
248 base::MessageLoopForUI message_loop_
;
251 DISALLOW_COPY_AND_ASSIGN(AutoConnectHandlerTest
);
256 const char* kConfigUnmanagedSharedConnected
=
257 "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"online\", "
258 " \"Security\": \"wpa\" }";
259 const char* kConfigManagedSharedConnectable
=
260 "{ \"GUID\": \"wifi1\", \"Type\": \"wifi\", \"State\": \"idle\", "
261 " \"Connectable\": true, \"Security\": \"wpa\" }";
263 const char* kPolicy
=
264 "[ { \"GUID\": \"wifi1\","
265 " \"Name\": \"wifi1\","
266 " \"Type\": \"WiFi\","
268 " \"Security\": \"WPA-PSK\","
269 " \"HexSSID\": \"7769666931\"," // "wifi1"
270 " \"Passphrase\": \"passphrase\""
274 const char* kPolicyCertPattern
=
275 "[ { \"GUID\": \"wifi1\","
276 " \"Name\": \"wifi1\","
277 " \"Type\": \"WiFi\","
279 " \"Security\": \"WPA-EAP\","
280 " \"HexSSID\": \"7769666931\"," // "wifi1"
282 " \"Outer\": \"EAP-TLS\","
283 " \"ClientCertType\": \"Pattern\","
284 " \"ClientCertPattern\": {"
286 " \"CommonName\": \"B CA\""
294 TEST_F(AutoConnectHandlerTest
, ReconnectOnCertLoading
) {
295 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected
));
296 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable
));
297 test_manager_client_
->SetBestServiceToConnect("wifi1");
299 // User login shouldn't trigger any change until the certificates and policy
301 LoginToRegularUser();
302 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi0"));
303 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
305 // Applying the policy which restricts autoconnect should disconnect from the
306 // shared, unmanaged network.
307 base::DictionaryValue global_config
;
308 global_config
.SetBooleanWithoutPathExpansion(
309 ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect
,
312 SetupPolicy(std::string(), // no network configs
313 base::DictionaryValue(), // no global config
314 true); // load as user policy
315 SetupPolicy(kPolicy
, global_config
, false /* load as device policy */);
316 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi0"));
317 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
319 // Certificate loading should trigger connecting to the 'best' network.
321 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi0"));
322 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi1"));
325 TEST_F(AutoConnectHandlerTest
, ReconnectOnCertPatternResolved
) {
326 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected
));
327 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable
));
328 test_manager_client_
->SetBestServiceToConnect("wifi0");
330 SetupPolicy(std::string(), // no device policy
331 base::DictionaryValue(), // no global config
332 false); // load as device policy
333 LoginToRegularUser();
335 SetupPolicy(kPolicyCertPattern
,
336 base::DictionaryValue(), // no global config
337 true); // load as user policy
339 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi0"));
340 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
342 test_manager_client_
->SetBestServiceToConnect("wifi1");
343 TestCertResolveObserver
observer(client_cert_resolver_
.get());
345 scoped_refptr
<net::X509Certificate
> cert
= ImportTestClientCert();
346 ASSERT_TRUE(cert
.get());
348 base::RunLoop().RunUntilIdle();
349 EXPECT_TRUE(observer
.DidNetworkPropertiesChange());
351 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi0"));
352 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi1"));
355 // Ensure that resolving of certificate patterns only triggers a reconnect if at
356 // least one pattern was resolved.
357 TEST_F(AutoConnectHandlerTest
, NoReconnectIfNoCertResolved
) {
358 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected
));
359 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable
));
360 test_manager_client_
->SetBestServiceToConnect("wifi0");
362 SetupPolicy(std::string(), // no device policy
363 base::DictionaryValue(), // no global config
364 false); // load as device policy
365 LoginToRegularUser();
368 base::DictionaryValue(), // no global config
369 true); // load as user policy
371 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi0"));
372 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
374 test_manager_client_
->SetBestServiceToConnect("wifi1");
375 TestCertResolveObserver
observer(client_cert_resolver_
.get());
376 scoped_refptr
<net::X509Certificate
> cert
= ImportTestClientCert();
377 ASSERT_TRUE(cert
.get());
379 base::RunLoop().RunUntilIdle();
380 EXPECT_FALSE(observer
.DidNetworkPropertiesChange());
382 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi0"));
383 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
386 TEST_F(AutoConnectHandlerTest
, DisconnectOnPolicyLoading
) {
387 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected
));
388 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable
));
390 // User login and certificate loading shouldn't trigger any change until the
392 LoginToRegularUser();
394 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi0"));
395 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
397 base::DictionaryValue global_config
;
398 global_config
.SetBooleanWithoutPathExpansion(
399 ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect
,
402 // Applying the policy which restricts autoconnect should disconnect from the
403 // shared, unmanaged network.
404 // Because no best service is set, the fake implementation of
405 // ConnectToBestServices will be a no-op.
406 SetupPolicy(kPolicy
, global_config
, false /* load as device policy */);
407 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi0"));
408 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
411 // After login a reconnect is triggered even if there is no managed network.
412 TEST_F(AutoConnectHandlerTest
, ReconnectAfterLogin
) {
413 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected
));
414 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable
));
415 test_manager_client_
->SetBestServiceToConnect("wifi1");
417 // User login and certificate loading shouldn't trigger any change until the
419 LoginToRegularUser();
421 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi0"));
422 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
424 // Applying an empty device policy will not trigger anything yet, until also
425 // the user policy is applied.
426 SetupPolicy(std::string(), // no network configs
427 base::DictionaryValue(), // no global config
428 false); // load as device policy
429 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi0"));
430 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
432 // Applying also an empty user policy should trigger connecting to the 'best'
434 SetupPolicy(std::string(), // no network configs
435 base::DictionaryValue(), // no global config
436 true); // load as user policy
437 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi0"));
438 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi1"));
441 TEST_F(AutoConnectHandlerTest
, ManualConnectAbortsReconnectAfterLogin
) {
442 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected
));
443 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable
));
444 test_manager_client_
->SetBestServiceToConnect("wifi1");
446 // User login and certificate loading shouldn't trigger any change until the
448 LoginToRegularUser();
450 SetupPolicy(std::string(), // no network configs
451 base::DictionaryValue(), // no global config
452 false); // load as device policy
454 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi0"));
455 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
457 // A manual connect request should prevent a reconnect after login.
458 auto_connect_handler_
->ConnectToNetworkRequested(
459 std::string() /* service_path */);
461 // Applying the user policy after login would usually trigger connecting to
462 // the 'best' network. But the manual connect prevents this.
463 SetupPolicy(std::string(), // no network configs
464 base::DictionaryValue(), // no global config
465 true); // load as user policy
466 EXPECT_EQ(shill::kStateOnline
, GetServiceState("wifi0"));
467 EXPECT_EQ(shill::kStateIdle
, GetServiceState("wifi1"));
470 } // namespace chromeos