client_cert_resolver_unittest.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 "chromeos/dbus/dbus_thread_manager.h" 15#include "chromeos/dbus/shill_profile_client.h" 16#include "chromeos/dbus/shill_service_client.h" 17#include "chromeos/login/login_state.h" 18#include "chromeos/network/managed_network_configuration_handler_impl.h" 19#include "chromeos/network/network_configuration_handler.h" 20#include "chromeos/network/network_profile_handler.h" 21#include "chromeos/network/network_state_handler.h" 22#include "crypto/nss_util.h" 23#include "net/base/crypto_module.h" 24#include "net/base/net_errors.h" 25#include "net/base/test_data_directory.h" 26#include "net/cert/nss_cert_database.h" 27#include "net/cert/x509_certificate.h" 28#include "net/test/cert_test_util.h" 29#include "testing/gtest/include/gtest/gtest.h" 30#include "third_party/cros_system_api/dbus/service_constants.h" 31 32namespace chromeos { 33 34namespace { 35 36const char* kWifiStub = "wifi_stub"; 37const char* kWifiSSID = "wifi_ssid"; 38const char* kUserProfilePath = "user_profile"; 39const char* kUserHash = "user_hash"; 40 41} // namespace 42 43class ClientCertResolverTest : public testing::Test { 44 public: 45 ClientCertResolverTest() {} 46 virtual ~ClientCertResolverTest() {} 47 48 virtual void SetUp() OVERRIDE { 49 ASSERT_TRUE(test_nssdb_.is_open()); 50 slot_ = net::NSSCertDatabase::GetInstance()->GetPublicModule(); 51 ASSERT_TRUE(slot_->os_module_handle()); 52 53 LoginState::Initialize(); 54 55 DBusThreadManager::InitializeWithStub(); 56 service_test_ = 57 DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); 58 profile_test_ = 59 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface(); 60 message_loop_.RunUntilIdle(); 61 service_test_->ClearServices(); 62 message_loop_.RunUntilIdle(); 63 64 CertLoader::Initialize(); 65 CertLoader* cert_loader = CertLoader::Get(); 66 cert_loader->InitializeTPMForTest(); 67 cert_loader->SetSlowTaskRunnerForTest(message_loop_.message_loop_proxy()); 68 cert_loader->SetCryptoTaskRunner(message_loop_.message_loop_proxy()); 69 } 70 71 virtual void TearDown() OVERRIDE { 72 client_cert_resolver_.reset(); 73 managed_config_handler_.reset(); 74 network_config_handler_.reset(); 75 network_profile_handler_.reset(); 76 network_state_handler_.reset(); 77 CertLoader::Shutdown(); 78 DBusThreadManager::Shutdown(); 79 LoginState::Shutdown(); 80 CleanupSlotContents(); 81 } 82 83 protected: 84 // Imports a CA cert (stored as PEM in test_ca_cert_pem_) and a client 85 // certificate signed by that CA. Its PKCS#11 ID is stored in 86 // |test_pkcs11_id_|. 87 void SetupTestCerts() { 88 // Import a CA cert. 89 net::NSSCertDatabase* cert_db = net::NSSCertDatabase::GetInstance(); 90 net::CertificateList ca_cert_list = 91 net::CreateCertificateListFromFile(net::GetTestCertsDirectory(), 92 "websocket_cacert.pem", 93 net::X509Certificate::FORMAT_AUTO); 94 ASSERT_TRUE(!ca_cert_list.empty()); 95 net::NSSCertDatabase::ImportCertFailureList failures; 96 EXPECT_TRUE(cert_db->ImportCACerts( 97 ca_cert_list, net::NSSCertDatabase::TRUST_DEFAULT, &failures)); 98 ASSERT_TRUE(failures.empty()) << net::ErrorToString(failures[0].net_error); 99 100 net::X509Certificate::GetPEMEncoded(ca_cert_list[0]->os_cert_handle(), 101 &test_ca_cert_pem_); 102 ASSERT_TRUE(!test_ca_cert_pem_.empty()); 103 104 // Import a client cert signed by that CA. 105 scoped_refptr<net::CryptoModule> crypt_module = cert_db->GetPrivateModule(); 106 std::string pkcs12_data; 107 ASSERT_TRUE(base::ReadFileToString( 108 net::GetTestCertsDirectory().Append("websocket_client_cert.p12"), 109 &pkcs12_data)); 110 111 net::CertificateList client_cert_list; 112 ASSERT_EQ(net::OK, 113 cert_db->ImportFromPKCS12(crypt_module.get(), 114 pkcs12_data, 115 string16(), 116 false, 117 &client_cert_list)); 118 ASSERT_TRUE(!client_cert_list.empty()); 119 test_pkcs11_id_ = CertLoader::GetPkcs11IdForCert(*client_cert_list[0]); 120 ASSERT_TRUE(!test_pkcs11_id_.empty()); 121 } 122 123 void SetupNetworkHandlers() { 124 network_state_handler_.reset(NetworkStateHandler::InitializeForTest()); 125 network_profile_handler_.reset(new NetworkProfileHandler()); 126 network_config_handler_.reset(new NetworkConfigurationHandler()); 127 managed_config_handler_.reset(new ManagedNetworkConfigurationHandlerImpl()); 128 client_cert_resolver_.reset(new ClientCertResolver()); 129 130 network_profile_handler_->Init(network_state_handler_.get()); 131 network_config_handler_->Init(network_state_handler_.get()); 132 managed_config_handler_->Init(network_state_handler_.get(), 133 network_profile_handler_.get(), 134 network_config_handler_.get()); 135 client_cert_resolver_->Init(network_state_handler_.get(), 136 managed_config_handler_.get()); 137 client_cert_resolver_->SetSlowTaskRunnerForTest( 138 message_loop_.message_loop_proxy()); 139 140 profile_test_->AddProfile(kUserProfilePath, kUserHash); 141 } 142 143 void SetupWifi() { 144 const bool add_to_visible = true; 145 const bool add_to_watchlist = true; 146 service_test_->AddService(kWifiStub, 147 kWifiSSID, 148 flimflam::kTypeWifi, 149 flimflam::kStateOnline, 150 add_to_visible, 151 add_to_watchlist); 152 service_test_->SetServiceProperty( 153 kWifiStub, flimflam::kGuidProperty, base::StringValue(kWifiStub)); 154 155 profile_test_->AddService(kUserProfilePath, kWifiStub); 156 } 157 158 // Setup a policy with a certificate pattern that matches any client cert that 159 // is signed by the test CA cert (stored in |test_ca_cert_pem_|). In 160 // particular it will match the test client cert. 161 void SetupPolicy() { 162 const char* kTestPolicyTemplate = 163 "[ { \"GUID\": \"wifi_stub\"," 164 " \"Name\": \"wifi_stub\"," 165 " \"Type\": \"WiFi\"," 166 " \"WiFi\": {" 167 " \"Security\": \"WPA-EAP\"," 168 " \"SSID\": \"wifi_ssid\"," 169 " \"EAP\": {" 170 " \"Outer\": \"EAP-TLS\"," 171 " \"ClientCertType\": \"Pattern\"," 172 " \"ClientCertPattern\": {" 173 " \"IssuerCAPEMs\": [ \"%s\" ]" 174 " }" 175 " }" 176 " }" 177 "} ]"; 178 std::string policy_json = 179 base::StringPrintf(kTestPolicyTemplate, test_ca_cert_pem_.c_str()); 180 181 std::string error; 182 scoped_ptr<base::Value> policy_value(base::JSONReader::ReadAndReturnError( 183 policy_json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, &error)); 184 ASSERT_TRUE(policy_value) << error; 185 186 base::ListValue* policy = NULL; 187 ASSERT_TRUE(policy_value->GetAsList(&policy)); 188 189 managed_config_handler_->SetPolicy( 190 onc::ONC_SOURCE_USER_POLICY, kUserHash, *policy); 191 } 192 193 void GetClientCertProperties(std::string* pkcs11_id) { 194 pkcs11_id->clear(); 195 const base::DictionaryValue* properties = 196 service_test_->GetServiceProperties(kWifiStub); 197 if (!properties) 198 return; 199 properties->GetStringWithoutPathExpansion(flimflam::kEapCertIdProperty, 200 pkcs11_id); 201 } 202 203 ShillServiceClient::TestInterface* service_test_; 204 ShillProfileClient::TestInterface* profile_test_; 205 std::string test_pkcs11_id_; 206 scoped_refptr<net::X509Certificate> test_ca_cert_; 207 std::string test_ca_cert_pem_; 208 base::MessageLoop message_loop_; 209 210 private: 211 void CleanupSlotContents() { 212 CERTCertList* cert_list = PK11_ListCertsInSlot(slot_->os_module_handle()); 213 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); 214 !CERT_LIST_END(node, cert_list); 215 node = CERT_LIST_NEXT(node)) { 216 scoped_refptr<net::X509Certificate> cert( 217 net::X509Certificate::CreateFromHandle( 218 node->cert, net::X509Certificate::OSCertHandles())); 219 net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(cert.get()); 220 } 221 CERT_DestroyCertList(cert_list); 222 } 223 224 scoped_ptr<NetworkStateHandler> network_state_handler_; 225 scoped_ptr<NetworkProfileHandler> network_profile_handler_; 226 scoped_ptr<NetworkConfigurationHandler> network_config_handler_; 227 scoped_ptr<ManagedNetworkConfigurationHandlerImpl> managed_config_handler_; 228 scoped_ptr<ClientCertResolver> client_cert_resolver_; 229 scoped_refptr<net::CryptoModule> slot_; 230 crypto::ScopedTestNSSDB test_nssdb_; 231 232 DISALLOW_COPY_AND_ASSIGN(ClientCertResolverTest); 233}; 234 235TEST_F(ClientCertResolverTest, NoMatchingCertificates) { 236 SetupNetworkHandlers(); 237 SetupPolicy(); 238 message_loop_.RunUntilIdle(); 239 240 SetupWifi(); 241 message_loop_.RunUntilIdle(); 242 243 // Verify that no client certificate was configured. 244 std::string pkcs11_id; 245 GetClientCertProperties(&pkcs11_id); 246 EXPECT_TRUE(pkcs11_id.empty()); 247} 248 249TEST_F(ClientCertResolverTest, ResolveOnInitialization) { 250 SetupTestCerts(); 251 SetupNetworkHandlers(); 252 SetupPolicy(); 253 message_loop_.RunUntilIdle(); 254 255 SetupWifi(); 256 message_loop_.RunUntilIdle(); 257 258 // Verify that the resolver positively matched the pattern in the policy with 259 // the test client cert and configured the network. 260 std::string pkcs11_id; 261 GetClientCertProperties(&pkcs11_id); 262 EXPECT_EQ(test_pkcs11_id_, pkcs11_id); 263} 264 265TEST_F(ClientCertResolverTest, ResolveAfterPolicyApplication) { 266 SetupTestCerts(); 267 SetupNetworkHandlers(); 268 message_loop_.RunUntilIdle(); 269 270 // The policy will trigger the creation of a new wifi service. 271 SetupPolicy(); 272 message_loop_.RunUntilIdle(); 273 274 // Verify that the resolver positively matched the pattern in the policy with 275 // the test client cert and configured the network. 276 std::string pkcs11_id; 277 GetClientCertProperties(&pkcs11_id); 278 EXPECT_EQ(test_pkcs11_id_, pkcs11_id); 279} 280 281} // namespace chromeos 282