client_cert_resolver_unittest.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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