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