1 // Copyright 2013 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 #include "chromeos/network/client_cert_resolver.h"
9 #include "base/file_util.h"
10 #include "base/files/file_path.h"
11 #include "base/json/json_reader.h"
12 #include "base/run_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/values.h"
15 #include "chromeos/cert_loader.h"
16 #include "chromeos/dbus/dbus_thread_manager.h"
17 #include "chromeos/dbus/shill_profile_client.h"
18 #include "chromeos/dbus/shill_service_client.h"
19 #include "chromeos/network/managed_network_configuration_handler_impl.h"
20 #include "chromeos/network/network_configuration_handler.h"
21 #include "chromeos/network/network_profile_handler.h"
22 #include "chromeos/network/network_state_handler.h"
23 #include "chromeos/tpm_token_loader.h"
24 #include "crypto/nss_util.h"
25 #include "crypto/nss_util_internal.h"
26 #include "net/base/crypto_module.h"
27 #include "net/base/net_errors.h"
28 #include "net/base/test_data_directory.h"
29 #include "net/cert/nss_cert_database_chromeos.h"
30 #include "net/cert/x509_certificate.h"
31 #include "net/test/cert_test_util.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 #include "third_party/cros_system_api/dbus/service_constants.h"
39 const char* kWifiStub
= "wifi_stub";
40 const char* kWifiSSID
= "wifi_ssid";
41 const char* kUserProfilePath
= "user_profile";
42 const char* kUserHash
= "user_hash";
46 class ClientCertResolverTest
: public testing::Test
{
48 ClientCertResolverTest() : service_test_(NULL
),
52 virtual ~ClientCertResolverTest() {}
54 virtual void SetUp() OVERRIDE
{
55 // Initialize NSS db for the user.
56 ASSERT_TRUE(user_
.constructed_successfully());
58 private_slot_
= crypto::GetPrivateSlotForChromeOSUser(
59 user_
.username_hash(),
60 base::Callback
<void(crypto::ScopedPK11Slot
)>());
61 ASSERT_TRUE(private_slot_
.get());
62 test_nssdb_
.reset(new net::NSSCertDatabaseChromeOS(
63 crypto::GetPublicSlotForChromeOSUser(user_
.username_hash()),
64 crypto::GetPrivateSlotForChromeOSUser(
65 user_
.username_hash(),
66 base::Callback
<void(crypto::ScopedPK11Slot
)>())));
67 test_nssdb_
->SetSlowTaskRunnerForTest(message_loop_
.message_loop_proxy());
69 DBusThreadManager::InitializeWithStub();
71 DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
73 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface();
74 base::RunLoop().RunUntilIdle();
75 service_test_
->ClearServices();
76 base::RunLoop().RunUntilIdle();
78 TPMTokenLoader::InitializeForTest();
80 CertLoader::Initialize();
81 CertLoader
* cert_loader_
= CertLoader::Get();
82 cert_loader_
->force_hardware_backed_for_test();
83 cert_loader_
->StartWithNSSDB(test_nssdb_
.get());
86 virtual void TearDown() OVERRIDE
{
87 client_cert_resolver_
.reset();
88 managed_config_handler_
.reset();
89 network_config_handler_
.reset();
90 network_profile_handler_
.reset();
91 network_state_handler_
.reset();
92 CertLoader::Shutdown();
93 TPMTokenLoader::Shutdown();
94 DBusThreadManager::Shutdown();
95 CleanupSlotContents();
99 // Imports a CA cert (stored as PEM in test_ca_cert_pem_) and a client
100 // certificate signed by that CA. Its PKCS#11 ID is stored in
101 // |test_pkcs11_id_|.
102 void SetupTestCerts() {
104 net::CertificateList ca_cert_list
=
105 net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
106 "websocket_cacert.pem",
107 net::X509Certificate::FORMAT_AUTO
);
108 ASSERT_TRUE(!ca_cert_list
.empty());
109 net::NSSCertDatabase::ImportCertFailureList failures
;
110 EXPECT_TRUE(test_nssdb_
->ImportCACerts(
111 ca_cert_list
, net::NSSCertDatabase::TRUST_DEFAULT
, &failures
));
112 ASSERT_TRUE(failures
.empty()) << net::ErrorToString(failures
[0].net_error
);
114 net::X509Certificate::GetPEMEncoded(ca_cert_list
[0]->os_cert_handle(),
116 ASSERT_TRUE(!test_ca_cert_pem_
.empty());
118 // Import a client cert signed by that CA.
119 std::string pkcs12_data
;
120 ASSERT_TRUE(base::ReadFileToString(
121 net::GetTestCertsDirectory().Append("websocket_client_cert.p12"),
124 net::CertificateList client_cert_list
;
125 scoped_refptr
<net::CryptoModule
> module(
126 net::CryptoModule::CreateFromHandle(private_slot_
.get()));
129 test_nssdb_
->ImportFromPKCS12(
130 module
, pkcs12_data
, base::string16(), false, &client_cert_list
));
131 ASSERT_TRUE(!client_cert_list
.empty());
132 test_pkcs11_id_
= CertLoader::GetPkcs11IdForCert(*client_cert_list
[0]);
133 ASSERT_TRUE(!test_pkcs11_id_
.empty());
136 void SetupNetworkHandlers() {
137 network_state_handler_
.reset(NetworkStateHandler::InitializeForTest());
138 network_profile_handler_
.reset(new NetworkProfileHandler());
139 network_config_handler_
.reset(new NetworkConfigurationHandler());
140 managed_config_handler_
.reset(new ManagedNetworkConfigurationHandlerImpl());
141 client_cert_resolver_
.reset(new ClientCertResolver());
143 network_profile_handler_
->Init(network_state_handler_
.get());
144 network_config_handler_
->Init(network_state_handler_
.get());
145 managed_config_handler_
->Init(network_state_handler_
.get(),
146 network_profile_handler_
.get(),
147 network_config_handler_
.get());
148 client_cert_resolver_
->Init(network_state_handler_
.get(),
149 managed_config_handler_
.get());
150 client_cert_resolver_
->SetSlowTaskRunnerForTest(
151 message_loop_
.message_loop_proxy());
153 profile_test_
->AddProfile(kUserProfilePath
, kUserHash
);
157 const bool add_to_visible
= true;
158 const bool add_to_watchlist
= true;
159 service_test_
->AddService(kWifiStub
,
165 service_test_
->SetServiceProperty(
166 kWifiStub
, shill::kGuidProperty
, base::StringValue(kWifiStub
));
168 profile_test_
->AddService(kUserProfilePath
, kWifiStub
);
171 // Setup a policy with a certificate pattern that matches any client cert that
172 // is signed by the test CA cert (stored in |test_ca_cert_pem_|). In
173 // particular it will match the test client cert.
175 const char* kTestPolicyTemplate
=
176 "[ { \"GUID\": \"wifi_stub\","
177 " \"Name\": \"wifi_stub\","
178 " \"Type\": \"WiFi\","
180 " \"Security\": \"WPA-EAP\","
181 " \"SSID\": \"wifi_ssid\","
183 " \"Outer\": \"EAP-TLS\","
184 " \"ClientCertType\": \"Pattern\","
185 " \"ClientCertPattern\": {"
186 " \"IssuerCAPEMs\": [ \"%s\" ]"
191 std::string policy_json
=
192 base::StringPrintf(kTestPolicyTemplate
, test_ca_cert_pem_
.c_str());
195 scoped_ptr
<base::Value
> policy_value(base::JSONReader::ReadAndReturnError(
196 policy_json
, base::JSON_ALLOW_TRAILING_COMMAS
, NULL
, &error
));
197 ASSERT_TRUE(policy_value
) << error
;
199 base::ListValue
* policy
= NULL
;
200 ASSERT_TRUE(policy_value
->GetAsList(&policy
));
202 managed_config_handler_
->SetPolicy(
203 onc::ONC_SOURCE_USER_POLICY
,
206 base::DictionaryValue() /* no global network config */);
209 void GetClientCertProperties(std::string
* pkcs11_id
) {
211 const base::DictionaryValue
* properties
=
212 service_test_
->GetServiceProperties(kWifiStub
);
215 properties
->GetStringWithoutPathExpansion(shill::kEapCertIdProperty
,
219 ShillServiceClient::TestInterface
* service_test_
;
220 ShillProfileClient::TestInterface
* profile_test_
;
221 std::string test_pkcs11_id_
;
222 scoped_refptr
<net::X509Certificate
> test_ca_cert_
;
223 std::string test_ca_cert_pem_
;
224 base::MessageLoop message_loop_
;
227 void CleanupSlotContents() {
228 CERTCertList
* cert_list
= PK11_ListCertsInSlot(private_slot_
.get());
229 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
230 !CERT_LIST_END(node
, cert_list
);
231 node
= CERT_LIST_NEXT(node
)) {
232 scoped_refptr
<net::X509Certificate
> cert(
233 net::X509Certificate::CreateFromHandle(
234 node
->cert
, net::X509Certificate::OSCertHandles()));
235 test_nssdb_
->DeleteCertAndKey(cert
.get());
237 CERT_DestroyCertList(cert_list
);
240 scoped_ptr
<NetworkStateHandler
> network_state_handler_
;
241 scoped_ptr
<NetworkProfileHandler
> network_profile_handler_
;
242 scoped_ptr
<NetworkConfigurationHandler
> network_config_handler_
;
243 scoped_ptr
<ManagedNetworkConfigurationHandlerImpl
> managed_config_handler_
;
244 scoped_ptr
<ClientCertResolver
> client_cert_resolver_
;
245 crypto::ScopedTestNSSChromeOSUser user_
;
246 scoped_ptr
<net::NSSCertDatabaseChromeOS
> test_nssdb_
;
247 crypto::ScopedPK11Slot private_slot_
;
249 DISALLOW_COPY_AND_ASSIGN(ClientCertResolverTest
);
252 TEST_F(ClientCertResolverTest
, NoMatchingCertificates
) {
253 SetupNetworkHandlers();
255 base::RunLoop().RunUntilIdle();
258 base::RunLoop().RunUntilIdle();
260 // Verify that no client certificate was configured.
261 std::string pkcs11_id
;
262 GetClientCertProperties(&pkcs11_id
);
263 EXPECT_TRUE(pkcs11_id
.empty());
266 TEST_F(ClientCertResolverTest
, ResolveOnInitialization
) {
268 SetupNetworkHandlers();
270 base::RunLoop().RunUntilIdle();
273 base::RunLoop().RunUntilIdle();
275 // Verify that the resolver positively matched the pattern in the policy with
276 // the test client cert and configured the network.
277 std::string pkcs11_id
;
278 GetClientCertProperties(&pkcs11_id
);
279 EXPECT_EQ(test_pkcs11_id_
, pkcs11_id
);
282 TEST_F(ClientCertResolverTest
, ResolveAfterPolicyApplication
) {
284 SetupNetworkHandlers();
285 base::RunLoop().RunUntilIdle();
287 // The policy will trigger the creation of a new wifi service.
289 base::RunLoop().RunUntilIdle();
291 // Verify that the resolver positively matched the pattern in the policy with
292 // the test client cert and configured the network.
293 std::string pkcs11_id
;
294 GetClientCertProperties(&pkcs11_id
);
295 EXPECT_EQ(test_pkcs11_id_
, pkcs11_id
);
298 } // namespace chromeos