OLD | NEW |
(Empty) | |
| 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.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 |
| 32 namespace chromeos { |
| 33 |
| 34 namespace { |
| 35 |
| 36 const char* kWifiStub = "wifi_stub"; |
| 37 const char* kWifiSSID = "wifi_ssid"; |
| 38 const char* kUserProfilePath = "user_profile"; |
| 39 const char* kUserHash = "user_hash"; |
| 40 |
| 41 } // namespace |
| 42 |
| 43 class 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(file_util::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 ManagedNetworkConfigurationHandler()); |
| 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<ManagedNetworkConfigurationHandler> 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 |
| 235 TEST_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 |
| 249 TEST_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 |
| 265 TEST_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 |
OLD | NEW |