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" 5 6#include <cert.h> 7#include <pk11pub.h> 8 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 "components/onc/onc_constants.h" 25#include "crypto/nss_util.h" 26#include "crypto/nss_util_internal.h" 27#include "net/base/crypto_module.h" 28#include "net/base/net_errors.h" 29#include "net/base/test_data_directory.h" 30#include "net/cert/nss_cert_database_chromeos.h" 31#include "net/cert/x509_certificate.h" 32#include "net/test/cert_test_util.h" 33#include "testing/gtest/include/gtest/gtest.h" 34#include "third_party/cros_system_api/dbus/service_constants.h" 35 36namespace chromeos { 37 38namespace { 39 40const char* kWifiStub = "wifi_stub"; 41const char* kWifiSSID = "wifi_ssid"; 42const char* kUserProfilePath = "user_profile"; 43const char* kUserHash = "user_hash"; 44 45} // namespace 46 47class ClientCertResolverTest : public testing::Test { 48 public: 49 ClientCertResolverTest() : service_test_(NULL), 50 profile_test_(NULL), 51 user_(kUserHash) { 52 } 53 virtual ~ClientCertResolverTest() {} 54 55 virtual void SetUp() OVERRIDE { 56 // Initialize NSS db for the user. 57 ASSERT_TRUE(user_.constructed_successfully()); 58 user_.FinishInit(); 59 private_slot_ = crypto::GetPrivateSlotForChromeOSUser( 60 user_.username_hash(), 61 base::Callback<void(crypto::ScopedPK11Slot)>()); 62 ASSERT_TRUE(private_slot_.get()); 63 test_nssdb_.reset(new net::NSSCertDatabaseChromeOS( 64 crypto::GetPublicSlotForChromeOSUser(user_.username_hash()), 65 crypto::GetPrivateSlotForChromeOSUser( 66 user_.username_hash(), 67 base::Callback<void(crypto::ScopedPK11Slot)>()))); 68 test_nssdb_->SetSlowTaskRunnerForTest(message_loop_.message_loop_proxy()); 69 70 DBusThreadManager::InitializeWithStub(); 71 service_test_ = 72 DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); 73 profile_test_ = 74 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface(); 75 base::RunLoop().RunUntilIdle(); 76 service_test_->ClearServices(); 77 base::RunLoop().RunUntilIdle(); 78 79 TPMTokenLoader::InitializeForTest(); 80 81 CertLoader::Initialize(); 82 CertLoader* cert_loader_ = CertLoader::Get(); 83 cert_loader_->force_hardware_backed_for_test(); 84 cert_loader_->StartWithNSSDB(test_nssdb_.get()); 85 } 86 87 virtual void TearDown() OVERRIDE { 88 client_cert_resolver_.reset(); 89 managed_config_handler_.reset(); 90 network_config_handler_.reset(); 91 network_profile_handler_.reset(); 92 network_state_handler_.reset(); 93 CertLoader::Shutdown(); 94 TPMTokenLoader::Shutdown(); 95 DBusThreadManager::Shutdown(); 96 CleanupSlotContents(); 97 } 98 99 protected: 100 // Imports a CA cert (stored as PEM in test_ca_cert_pem_) and a client 101 // certificate signed by that CA. Its PKCS#11 ID is stored in 102 // |test_pkcs11_id_|. 103 void SetupTestCerts() { 104 // Import a CA cert. 105 net::CertificateList ca_cert_list = 106 net::CreateCertificateListFromFile(net::GetTestCertsDirectory(), 107 "websocket_cacert.pem", 108 net::X509Certificate::FORMAT_AUTO); 109 ASSERT_TRUE(!ca_cert_list.empty()); 110 net::NSSCertDatabase::ImportCertFailureList failures; 111 EXPECT_TRUE(test_nssdb_->ImportCACerts( 112 ca_cert_list, net::NSSCertDatabase::TRUST_DEFAULT, &failures)); 113 ASSERT_TRUE(failures.empty()) << net::ErrorToString(failures[0].net_error); 114 115 net::X509Certificate::GetPEMEncoded(ca_cert_list[0]->os_cert_handle(), 116 &test_ca_cert_pem_); 117 ASSERT_TRUE(!test_ca_cert_pem_.empty()); 118 119 // Import a client cert signed by that CA. 120 std::string pkcs12_data; 121 ASSERT_TRUE(base::ReadFileToString( 122 net::GetTestCertsDirectory().Append("websocket_client_cert.p12"), 123 &pkcs12_data)); 124 125 net::CertificateList client_cert_list; 126 scoped_refptr<net::CryptoModule> module( 127 net::CryptoModule::CreateFromHandle(private_slot_.get())); 128 ASSERT_EQ( 129 net::OK, 130 test_nssdb_->ImportFromPKCS12( 131 module, pkcs12_data, base::string16(), false, &client_cert_list)); 132 ASSERT_TRUE(!client_cert_list.empty()); 133 test_pkcs11_id_ = CertLoader::GetPkcs11IdForCert(*client_cert_list[0]); 134 ASSERT_TRUE(!test_pkcs11_id_.empty()); 135 } 136 137 void SetupNetworkHandlers() { 138 network_state_handler_.reset(NetworkStateHandler::InitializeForTest()); 139 network_profile_handler_.reset(new NetworkProfileHandler()); 140 network_config_handler_.reset(new NetworkConfigurationHandler()); 141 managed_config_handler_.reset(new ManagedNetworkConfigurationHandlerImpl()); 142 client_cert_resolver_.reset(new ClientCertResolver()); 143 144 network_profile_handler_->Init(); 145 network_config_handler_->Init(network_state_handler_.get()); 146 managed_config_handler_->Init(network_state_handler_.get(), 147 network_profile_handler_.get(), 148 network_config_handler_.get(), 149 NULL /* network_device_handler */); 150 client_cert_resolver_->Init(network_state_handler_.get(), 151 managed_config_handler_.get()); 152 client_cert_resolver_->SetSlowTaskRunnerForTest( 153 message_loop_.message_loop_proxy()); 154 155 profile_test_->AddProfile(kUserProfilePath, kUserHash); 156 } 157 158 void SetupWifi() { 159 const bool add_to_visible = true; 160 service_test_->AddService(kWifiStub, 161 kWifiSSID, 162 shill::kTypeWifi, 163 shill::kStateOnline, 164 add_to_visible); 165 service_test_->SetServiceProperty( 166 kWifiStub, shill::kGuidProperty, base::StringValue(kWifiStub)); 167 168 profile_test_->AddService(kUserProfilePath, kWifiStub); 169 } 170 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. 174 void SetupPolicy() { 175 const char* kTestPolicyTemplate = 176 "[ { \"GUID\": \"wifi_stub\"," 177 " \"Name\": \"wifi_stub\"," 178 " \"Type\": \"WiFi\"," 179 " \"WiFi\": {" 180 " \"Security\": \"WPA-EAP\"," 181 " \"SSID\": \"wifi_ssid\"," 182 " \"EAP\": {" 183 " \"Outer\": \"EAP-TLS\"," 184 " \"ClientCertType\": \"Pattern\"," 185 " \"ClientCertPattern\": {" 186 " \"IssuerCAPEMs\": [ \"%s\" ]" 187 " }" 188 " }" 189 " }" 190 "} ]"; 191 std::string policy_json = 192 base::StringPrintf(kTestPolicyTemplate, test_ca_cert_pem_.c_str()); 193 194 std::string error; 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; 198 199 base::ListValue* policy = NULL; 200 ASSERT_TRUE(policy_value->GetAsList(&policy)); 201 202 managed_config_handler_->SetPolicy( 203 onc::ONC_SOURCE_USER_POLICY, 204 kUserHash, 205 *policy, 206 base::DictionaryValue() /* no global network config */); 207 } 208 209 void GetClientCertProperties(std::string* pkcs11_id) { 210 pkcs11_id->clear(); 211 const base::DictionaryValue* properties = 212 service_test_->GetServiceProperties(kWifiStub); 213 if (!properties) 214 return; 215 properties->GetStringWithoutPathExpansion(shill::kEapCertIdProperty, 216 pkcs11_id); 217 } 218 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_; 225 226 private: 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()); 236 } 237 CERT_DestroyCertList(cert_list); 238 } 239 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_; 248 249 DISALLOW_COPY_AND_ASSIGN(ClientCertResolverTest); 250}; 251 252TEST_F(ClientCertResolverTest, NoMatchingCertificates) { 253 SetupNetworkHandlers(); 254 SetupPolicy(); 255 base::RunLoop().RunUntilIdle(); 256 257 SetupWifi(); 258 base::RunLoop().RunUntilIdle(); 259 260 // Verify that no client certificate was configured. 261 std::string pkcs11_id; 262 GetClientCertProperties(&pkcs11_id); 263 EXPECT_TRUE(pkcs11_id.empty()); 264} 265 266TEST_F(ClientCertResolverTest, ResolveOnInitialization) { 267 SetupTestCerts(); 268 SetupNetworkHandlers(); 269 SetupPolicy(); 270 base::RunLoop().RunUntilIdle(); 271 272 SetupWifi(); 273 base::RunLoop().RunUntilIdle(); 274 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); 280} 281 282TEST_F(ClientCertResolverTest, ResolveAfterPolicyApplication) { 283 SetupTestCerts(); 284 SetupNetworkHandlers(); 285 base::RunLoop().RunUntilIdle(); 286 287 // The policy will trigger the creation of a new wifi service. 288 SetupPolicy(); 289 base::RunLoop().RunUntilIdle(); 290 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); 296} 297 298} // namespace chromeos 299