OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "chromeos/network/auto_connect_handler.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/bind.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" | |
39 | |
40 // http://crbug.com/418369 | |
41 #ifdef NDEBUG | |
42 | |
43 namespace chromeos { | |
44 | |
45 namespace { | |
46 | |
47 const char* kUserHash = "user_hash"; | |
48 | |
49 void ConfigureCallback(const dbus::ObjectPath& result) { | |
stevenjb
2014/11/07 17:19:06
nit: Might be worth making these member functions
pneubeck (no reviews)
2014/11/08 16:49:55
these are only used for the test setup and don't t
| |
50 } | |
51 | |
52 void ConfigureErrorCallback(const std::string& error_name, | |
53 const std::string& error_message) { | |
54 } | |
55 | |
56 class TestCertResolveObserver : public ClientCertResolver::Observer { | |
57 public: | |
58 explicit TestCertResolveObserver(ClientCertResolver* cert_resolver) | |
59 : changed_network_properties_(false), cert_resolver_(cert_resolver) { | |
60 cert_resolver_->AddObserver(this); | |
61 } | |
62 | |
63 void ResolveRequestCompleted(bool changed_network_properties) override { | |
64 cert_resolver_->RemoveObserver(this); | |
65 changed_network_properties_ = changed_network_properties; | |
66 } | |
67 | |
68 bool DidNetworkPropertiesChange() { return changed_network_properties_; } | |
69 | |
70 private: | |
71 bool changed_network_properties_; | |
72 ClientCertResolver* cert_resolver_; | |
73 }; | |
74 | |
75 } // namespace | |
76 | |
77 class AutoConnectHandlerTest : public testing::Test { | |
78 public: | |
79 AutoConnectHandlerTest() | |
80 : test_manager_client_(nullptr), test_service_client_(nullptr) {} | |
81 | |
82 void SetUp() override { | |
83 ASSERT_TRUE(test_nssdb_.is_open()); | |
84 | |
85 // Use the same DB for public and private slot. | |
86 test_nsscertdb_.reset(new net::NSSCertDatabaseChromeOS( | |
87 crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_.slot())), | |
88 crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_.slot())))); | |
89 test_nsscertdb_->SetSlowTaskRunnerForTest(message_loop_.task_runner()); | |
90 | |
91 CertLoader::Initialize(); | |
92 CertLoader::ForceHardwareBackedForTesting(); | |
93 | |
94 DBusThreadManager::Initialize(); | |
95 DBusThreadManager* dbus_manager = DBusThreadManager::Get(); | |
96 test_manager_client_ = | |
97 dbus_manager->GetShillManagerClient()->GetTestInterface(); | |
98 test_service_client_ = | |
99 dbus_manager->GetShillServiceClient()->GetTestInterface(); | |
100 | |
101 test_manager_client_->AddTechnology(shill::kTypeWifi, true /* enabled */); | |
102 dbus_manager->GetShillDeviceClient()->GetTestInterface()->AddDevice( | |
103 "/device/wifi1", shill::kTypeWifi, "wifi_device1"); | |
104 test_manager_client_->AddTechnology(shill::kTypeCellular, | |
105 true /* enabled */); | |
106 dbus_manager->GetShillProfileClient()->GetTestInterface()->AddProfile( | |
107 "shared_profile_path", std::string() /* shared profile */); | |
108 dbus_manager->GetShillProfileClient()->GetTestInterface()->AddProfile( | |
109 "user_profile_path", kUserHash); | |
110 | |
111 base::RunLoop().RunUntilIdle(); | |
112 LoginState::Initialize(); | |
113 network_state_handler_.reset(NetworkStateHandler::InitializeForTest()); | |
114 network_config_handler_.reset( | |
115 NetworkConfigurationHandler::InitializeForTest( | |
116 network_state_handler_.get())); | |
117 | |
118 network_profile_handler_.reset(new NetworkProfileHandler()); | |
119 network_profile_handler_->Init(); | |
120 | |
121 managed_config_handler_.reset(new ManagedNetworkConfigurationHandlerImpl()); | |
122 managed_config_handler_->Init( | |
123 network_state_handler_.get(), network_profile_handler_.get(), | |
124 network_config_handler_.get(), nullptr /* network_device_handler */); | |
125 | |
126 client_cert_resolver_.reset(new ClientCertResolver()); | |
127 client_cert_resolver_->Init(network_state_handler_.get(), | |
128 managed_config_handler_.get()); | |
129 client_cert_resolver_->SetSlowTaskRunnerForTest( | |
130 message_loop_.task_runner()); | |
131 | |
132 auto_connect_handler_.reset(new AutoConnectHandler()); | |
133 auto_connect_handler_->Init(client_cert_resolver_.get(), | |
134 nullptr, // no connection handler | |
135 network_state_handler_.get(), | |
136 managed_config_handler_.get()); | |
137 | |
138 base::RunLoop().RunUntilIdle(); | |
139 } | |
140 | |
141 void TearDown() override { | |
142 auto_connect_handler_.reset(); | |
143 client_cert_resolver_.reset(); | |
144 managed_config_handler_.reset(); | |
145 network_profile_handler_.reset(); | |
146 network_config_handler_.reset(); | |
147 network_state_handler_.reset(); | |
148 CertLoader::Shutdown(); | |
149 LoginState::Shutdown(); | |
150 DBusThreadManager::Shutdown(); | |
151 } | |
152 | |
153 protected: | |
154 bool Configure(const std::string& json_string) { | |
155 scoped_ptr<base::DictionaryValue> json_dict = | |
156 onc::ReadDictionaryFromJson(json_string); | |
157 if (!json_dict) { | |
158 LOG(ERROR) << "Error parsing json: " << json_string; | |
159 return false; | |
160 } | |
161 DBusThreadManager::Get()->GetShillManagerClient()->ConfigureService( | |
162 *json_dict, base::Bind(&ConfigureCallback), | |
163 base::Bind(&ConfigureErrorCallback)); | |
164 base::RunLoop().RunUntilIdle(); | |
165 return true; | |
166 } | |
167 | |
168 std::string GetServiceState(const std::string& service_path) { | |
169 const base::DictionaryValue* properties = | |
170 test_service_client_->GetServiceProperties(service_path); | |
171 std::string result; | |
172 if (properties) | |
173 properties->GetStringWithoutPathExpansion(shill::kStateProperty, &result); | |
174 return result; | |
175 } | |
176 | |
177 void StartCertLoader() { | |
178 CertLoader::Get()->StartWithNSSDB(test_nsscertdb_.get()); | |
179 base::RunLoop().RunUntilIdle(); | |
180 } | |
181 | |
182 void LoginToRegularUser() { | |
183 LoginState::Get()->SetLoggedInState(LoginState::LOGGED_IN_ACTIVE, | |
184 LoginState::LOGGED_IN_USER_REGULAR); | |
185 base::RunLoop().RunUntilIdle(); | |
186 } | |
187 | |
188 scoped_refptr<net::X509Certificate> ImportTestClientCert() { | |
189 net::CertificateList ca_cert_list = net::CreateCertificateListFromFile( | |
190 net::GetTestCertsDirectory(), "client_1_ca.pem", | |
191 net::X509Certificate::FORMAT_AUTO); | |
192 if (ca_cert_list.empty()) { | |
193 LOG(ERROR) << "No CA cert loaded."; | |
194 return nullptr; | |
195 } | |
196 net::NSSCertDatabase::ImportCertFailureList failures; | |
197 EXPECT_TRUE(test_nsscertdb_->ImportCACerts( | |
198 ca_cert_list, net::NSSCertDatabase::TRUST_DEFAULT, &failures)); | |
199 if (!failures.empty()) { | |
200 LOG(ERROR) << net::ErrorToString(failures[0].net_error); | |
201 return nullptr; | |
202 } | |
203 | |
204 // Import a client cert signed by that CA. | |
205 scoped_refptr<net::X509Certificate> client_cert( | |
206 net::ImportClientCertAndKeyFromFile(net::GetTestCertsDirectory(), | |
207 "client_1.pem", "client_1.pk8", | |
208 test_nssdb_.slot())); | |
209 return client_cert; | |
210 } | |
211 | |
212 void SetupPolicy(const std::string& network_configs_json, | |
213 const base::DictionaryValue& global_config, | |
214 bool user_policy) { | |
215 scoped_ptr<base::ListValue> network_configs(new base::ListValue); | |
216 if (!network_configs_json.empty()) { | |
217 std::string error; | |
218 base::Value* network_configs_value = base::JSONReader::ReadAndReturnError( | |
219 network_configs_json, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, | |
220 &error); | |
221 ASSERT_TRUE(network_configs_value) << error; | |
222 base::ListValue* network_configs_list = nullptr; | |
223 ASSERT_TRUE(network_configs_value->GetAsList(&network_configs_list)); | |
224 network_configs.reset(network_configs_list); | |
225 } | |
226 | |
227 if (user_policy) { | |
228 managed_config_handler_->SetPolicy(::onc::ONC_SOURCE_USER_POLICY, | |
229 kUserHash, *network_configs, | |
230 global_config); | |
231 } else { | |
232 managed_config_handler_->SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, | |
233 std::string(), // no username hash | |
234 *network_configs, global_config); | |
235 } | |
236 base::RunLoop().RunUntilIdle(); | |
237 } | |
238 | |
239 scoped_ptr<AutoConnectHandler> auto_connect_handler_; | |
240 scoped_ptr<ClientCertResolver> client_cert_resolver_; | |
241 scoped_ptr<NetworkStateHandler> network_state_handler_; | |
242 scoped_ptr<NetworkConfigurationHandler> network_config_handler_; | |
243 scoped_ptr<ManagedNetworkConfigurationHandlerImpl> managed_config_handler_; | |
244 scoped_ptr<NetworkProfileHandler> network_profile_handler_; | |
245 ShillManagerClient::TestInterface* test_manager_client_; | |
246 ShillServiceClient::TestInterface* test_service_client_; | |
247 crypto::ScopedTestNSSDB test_nssdb_; | |
248 scoped_ptr<net::NSSCertDatabaseChromeOS> test_nsscertdb_; | |
249 base::MessageLoopForUI message_loop_; | |
250 | |
251 private: | |
252 DISALLOW_COPY_AND_ASSIGN(AutoConnectHandlerTest); | |
253 }; | |
254 | |
255 namespace { | |
256 | |
257 const char* kConfigUnmanagedSharedConnected = | |
258 "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"online\", " | |
259 " \"Security\": \"wpa\" }"; | |
260 const char* kConfigManagedSharedConnectable = | |
261 "{ \"GUID\": \"wifi1\", \"Type\": \"wifi\", \"State\": \"idle\", " | |
262 " \"Connectable\": true, \"Security\": \"wpa\" }"; | |
263 | |
264 const char* kPolicy = | |
265 "[ { \"GUID\": \"wifi1\"," | |
266 " \"Name\": \"wifi1\"," | |
267 " \"Type\": \"WiFi\"," | |
268 " \"WiFi\": {" | |
269 " \"Security\": \"WPA-PSK\"," | |
270 " \"SSID\": \"wifi1\"," | |
271 " \"Passphrase\": \"passphrase\"" | |
272 " }" | |
273 "} ]"; | |
274 | |
275 const char* kPolicyCertPattern = | |
276 "[ { \"GUID\": \"wifi1\"," | |
277 " \"Name\": \"wifi1\"," | |
278 " \"Type\": \"WiFi\"," | |
279 " \"WiFi\": {" | |
280 " \"Security\": \"WPA-EAP\"," | |
281 " \"SSID\": \"wifi1\"," | |
282 " \"EAP\": {" | |
283 " \"Outer\": \"EAP-TLS\"," | |
284 " \"ClientCertType\": \"Pattern\"," | |
285 " \"ClientCertPattern\": {" | |
286 " \"Issuer\": {" | |
287 " \"CommonName\": \"B CA\"" | |
288 " }" | |
289 " }" | |
290 " }" | |
291 " }" | |
292 "} ]"; | |
293 } // namespace | |
294 | |
295 TEST_F(AutoConnectHandlerTest, ReconnectOnCertLoading) { | |
296 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected)); | |
297 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable)); | |
298 test_manager_client_->SetBestServiceToConnect("wifi1"); | |
299 | |
300 // User login shouldn't trigger any change until the certificates and policy | |
301 // are loaded. | |
302 LoginToRegularUser(); | |
303 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0")); | |
304 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1")); | |
305 | |
306 // Applying the policy which restricts autoconnect should disconnect from the | |
307 // shared, unmanaged network. | |
308 base::DictionaryValue global_config; | |
309 global_config.SetBooleanWithoutPathExpansion( | |
310 ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect, | |
311 true); | |
312 | |
313 SetupPolicy(std::string(), // no network configs | |
314 base::DictionaryValue(), // no global config | |
315 true); // load as user policy | |
316 SetupPolicy(kPolicy, global_config, false /* load as device policy */); | |
317 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi0")); | |
318 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1")); | |
319 | |
320 // Certificate loading should trigger connecting to the 'best' network. | |
321 StartCertLoader(); | |
322 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi0")); | |
323 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi1")); | |
324 } | |
325 | |
326 TEST_F(AutoConnectHandlerTest, ReconnectOnCertPatternResolved) { | |
327 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected)); | |
328 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable)); | |
329 test_manager_client_->SetBestServiceToConnect("wifi0"); | |
330 | |
331 SetupPolicy(std::string(), // no device policy | |
332 base::DictionaryValue(), // no global config | |
333 false); // load as device policy | |
334 LoginToRegularUser(); | |
335 StartCertLoader(); | |
336 SetupPolicy(kPolicyCertPattern, | |
337 base::DictionaryValue(), // no global config | |
338 true); // load as user policy | |
339 | |
340 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0")); | |
341 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1")); | |
342 | |
343 test_manager_client_->SetBestServiceToConnect("wifi1"); | |
344 TestCertResolveObserver observer(client_cert_resolver_.get()); | |
345 | |
346 scoped_refptr<net::X509Certificate> cert = ImportTestClientCert(); | |
347 ASSERT_TRUE(cert.get()); | |
348 | |
349 base::RunLoop().RunUntilIdle(); | |
350 EXPECT_TRUE(observer.DidNetworkPropertiesChange()); | |
351 | |
352 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi0")); | |
353 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi1")); | |
354 } | |
355 | |
356 // Ensure that resolving of certificate patterns only triggers a reconnect if at | |
357 // least one pattern was resolved. | |
358 TEST_F(AutoConnectHandlerTest, NoReconnectIfNoCertResolved) { | |
359 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected)); | |
360 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable)); | |
361 test_manager_client_->SetBestServiceToConnect("wifi0"); | |
362 | |
363 SetupPolicy(std::string(), // no device policy | |
364 base::DictionaryValue(), // no global config | |
365 false); // load as device policy | |
366 LoginToRegularUser(); | |
367 StartCertLoader(); | |
368 SetupPolicy(kPolicy, | |
369 base::DictionaryValue(), // no global config | |
370 true); // load as user policy | |
371 | |
372 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0")); | |
373 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1")); | |
374 | |
375 test_manager_client_->SetBestServiceToConnect("wifi1"); | |
376 TestCertResolveObserver observer(client_cert_resolver_.get()); | |
377 scoped_refptr<net::X509Certificate> cert = ImportTestClientCert(); | |
378 ASSERT_TRUE(cert.get()); | |
379 | |
380 base::RunLoop().RunUntilIdle(); | |
381 EXPECT_FALSE(observer.DidNetworkPropertiesChange()); | |
382 | |
383 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0")); | |
384 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1")); | |
385 } | |
386 | |
387 TEST_F(AutoConnectHandlerTest, DisconnectOnPolicyLoading) { | |
388 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected)); | |
389 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable)); | |
390 | |
391 // User login and certificate loading shouldn't trigger any change until the | |
392 // policy is loaded. | |
393 LoginToRegularUser(); | |
394 StartCertLoader(); | |
395 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0")); | |
396 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1")); | |
397 | |
398 base::DictionaryValue global_config; | |
399 global_config.SetBooleanWithoutPathExpansion( | |
400 ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect, | |
401 true); | |
402 | |
403 // Applying the policy which restricts autoconnect should disconnect from the | |
404 // shared, unmanaged network. | |
405 // Because no best service is set, the fake implementation of | |
406 // ConnectToBestServices will be a no-op. | |
407 SetupPolicy(kPolicy, global_config, false /* load as device policy */); | |
408 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi0")); | |
409 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1")); | |
410 } | |
411 | |
412 // After login a reconnect is triggered even if there is no managed network. | |
413 TEST_F(AutoConnectHandlerTest, ReconnectAfterLogin) { | |
414 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected)); | |
415 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable)); | |
416 test_manager_client_->SetBestServiceToConnect("wifi1"); | |
417 | |
418 // User login and certificate loading shouldn't trigger any change until the | |
419 // policy is loaded. | |
420 LoginToRegularUser(); | |
421 StartCertLoader(); | |
422 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0")); | |
423 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1")); | |
424 | |
425 // Applying an empty device policy will not trigger anything yet, until also | |
426 // the user policy is applied. | |
427 SetupPolicy(std::string(), // no network configs | |
428 base::DictionaryValue(), // no global config | |
429 false); // load as device policy | |
430 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0")); | |
431 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1")); | |
432 | |
433 // Applying also an empty user policy should trigger connecting to the 'best' | |
434 // network. | |
435 SetupPolicy(std::string(), // no network configs | |
436 base::DictionaryValue(), // no global config | |
437 true); // load as user policy | |
438 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi0")); | |
439 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi1")); | |
440 } | |
441 | |
442 TEST_F(AutoConnectHandlerTest, ManualConnectAbortsReconnectAfterLogin) { | |
443 EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected)); | |
444 EXPECT_TRUE(Configure(kConfigManagedSharedConnectable)); | |
445 test_manager_client_->SetBestServiceToConnect("wifi1"); | |
446 | |
447 // User login and certificate loading shouldn't trigger any change until the | |
448 // policy is loaded. | |
449 LoginToRegularUser(); | |
450 StartCertLoader(); | |
451 SetupPolicy(std::string(), // no network configs | |
452 base::DictionaryValue(), // no global config | |
453 false); // load as device policy | |
454 | |
455 EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0")); | |
456 EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1")); | |
457 | |
458 // A manual connect request should prevent a reconnect after login. | |
459 auto_connect_handler_->ConnectToNetworkRequested(); | |
460 | |
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")); | |
468 } | |
469 | |
470 } // namespace chromeos | |
471 | |
472 #endif | |
OLD | NEW |