client_cert_resolver_unittest.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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_manager_client.h"
18#include "chromeos/dbus/shill_profile_client.h"
19#include "chromeos/dbus/shill_service_client.h"
20#include "chromeos/network/managed_network_configuration_handler_impl.h"
21#include "chromeos/network/network_configuration_handler.h"
22#include "chromeos/network/network_profile_handler.h"
23#include "chromeos/network/network_state_handler.h"
24#include "chromeos/tpm_token_loader.h"
25#include "components/onc/onc_constants.h"
26#include "crypto/nss_util_internal.h"
27#include "crypto/scoped_test_nss_chromeos_user.h"
28#include "net/base/crypto_module.h"
29#include "net/base/net_errors.h"
30#include "net/base/test_data_directory.h"
31#include "net/cert/nss_cert_database_chromeos.h"
32#include "net/cert/x509_certificate.h"
33#include "net/test/cert_test_util.h"
34#include "testing/gtest/include/gtest/gtest.h"
35#include "third_party/cros_system_api/dbus/service_constants.h"
36
37namespace chromeos {
38
39namespace {
40
41const char* kWifiStub = "wifi_stub";
42const char* kWifiSSID = "wifi_ssid";
43const char* kUserProfilePath = "user_profile";
44const char* kUserHash = "user_hash";
45
46}  // namespace
47
48class ClientCertResolverTest : public testing::Test {
49 public:
50  ClientCertResolverTest() : service_test_(NULL),
51                             profile_test_(NULL),
52                             cert_loader_(NULL),
53                             user_(kUserHash) {
54  }
55  virtual ~ClientCertResolverTest() {}
56
57  virtual void SetUp() OVERRIDE {
58    // Initialize NSS db for the user.
59    ASSERT_TRUE(user_.constructed_successfully());
60    user_.FinishInit();
61    private_slot_ = crypto::GetPrivateSlotForChromeOSUser(
62        user_.username_hash(),
63        base::Callback<void(crypto::ScopedPK11Slot)>());
64    ASSERT_TRUE(private_slot_.get());
65    test_nssdb_.reset(new net::NSSCertDatabaseChromeOS(
66        crypto::GetPublicSlotForChromeOSUser(user_.username_hash()),
67        crypto::GetPrivateSlotForChromeOSUser(
68            user_.username_hash(),
69            base::Callback<void(crypto::ScopedPK11Slot)>())));
70    test_nssdb_->SetSlowTaskRunnerForTest(message_loop_.message_loop_proxy());
71
72    DBusThreadManager::InitializeWithStub();
73    service_test_ =
74        DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
75    profile_test_ =
76        DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface();
77    base::RunLoop().RunUntilIdle();
78    service_test_->ClearServices();
79    base::RunLoop().RunUntilIdle();
80
81    TPMTokenLoader::InitializeForTest();
82
83    CertLoader::Initialize();
84    cert_loader_ = CertLoader::Get();
85    cert_loader_->force_hardware_backed_for_test();
86  }
87
88  virtual void TearDown() OVERRIDE {
89    client_cert_resolver_.reset();
90    managed_config_handler_.reset();
91    network_config_handler_.reset();
92    network_profile_handler_.reset();
93    network_state_handler_.reset();
94    CertLoader::Shutdown();
95    TPMTokenLoader::Shutdown();
96    DBusThreadManager::Shutdown();
97    CleanupSlotContents();
98  }
99
100 protected:
101  void StartCertLoader() {
102    cert_loader_->StartWithNSSDB(test_nssdb_.get());
103    if (test_client_cert_) {
104      int slot_id = 0;
105      const std::string pkcs11_id =
106          CertLoader::GetPkcs11IdAndSlotForCert(*test_client_cert_, &slot_id);
107      test_cert_id_ = base::StringPrintf("%i:%s", slot_id, pkcs11_id.c_str());
108    }
109  }
110
111  // Imports a CA cert (stored as PEM in test_ca_cert_pem_) and a client
112  // certificate signed by that CA. Its PKCS#11 ID is stored in
113  // |test_cert_id_|.
114  void SetupTestCerts() {
115    // Import a CA cert.
116    net::CertificateList ca_cert_list =
117        net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
118                                           "websocket_cacert.pem",
119                                           net::X509Certificate::FORMAT_AUTO);
120    ASSERT_TRUE(!ca_cert_list.empty());
121    net::NSSCertDatabase::ImportCertFailureList failures;
122    EXPECT_TRUE(test_nssdb_->ImportCACerts(
123        ca_cert_list, net::NSSCertDatabase::TRUST_DEFAULT, &failures));
124    ASSERT_TRUE(failures.empty()) << net::ErrorToString(failures[0].net_error);
125
126    net::X509Certificate::GetPEMEncoded(ca_cert_list[0]->os_cert_handle(),
127                                        &test_ca_cert_pem_);
128    ASSERT_TRUE(!test_ca_cert_pem_.empty());
129
130    // Import a client cert signed by that CA.
131    std::string pkcs12_data;
132    ASSERT_TRUE(base::ReadFileToString(
133        net::GetTestCertsDirectory().Append("websocket_client_cert.p12"),
134        &pkcs12_data));
135
136    net::CertificateList client_cert_list;
137    scoped_refptr<net::CryptoModule> module(
138        net::CryptoModule::CreateFromHandle(private_slot_.get()));
139    ASSERT_EQ(
140        net::OK,
141        test_nssdb_->ImportFromPKCS12(
142            module, pkcs12_data, base::string16(), false, &client_cert_list));
143    ASSERT_TRUE(!client_cert_list.empty());
144    test_client_cert_ = client_cert_list[0];
145  }
146
147  void SetupNetworkHandlers() {
148    network_state_handler_.reset(NetworkStateHandler::InitializeForTest());
149    network_profile_handler_.reset(new NetworkProfileHandler());
150    network_config_handler_.reset(new NetworkConfigurationHandler());
151    managed_config_handler_.reset(new ManagedNetworkConfigurationHandlerImpl());
152    client_cert_resolver_.reset(new ClientCertResolver());
153
154    network_profile_handler_->Init();
155    network_config_handler_->Init(network_state_handler_.get());
156    managed_config_handler_->Init(network_state_handler_.get(),
157                                  network_profile_handler_.get(),
158                                  network_config_handler_.get(),
159                                  NULL /* network_device_handler */);
160    client_cert_resolver_->Init(network_state_handler_.get(),
161                                managed_config_handler_.get());
162    client_cert_resolver_->SetSlowTaskRunnerForTest(
163        message_loop_.message_loop_proxy());
164
165    profile_test_->AddProfile(kUserProfilePath, kUserHash);
166  }
167
168  void SetupWifi() {
169    service_test_->SetServiceProperties(kWifiStub,
170                                        kWifiStub,
171                                        kWifiSSID,
172                                        shill::kTypeWifi,
173                                        shill::kStateOnline,
174                                        true /* visible */);
175    // Set an arbitrary cert id, so that we can check afterwards whether we
176    // cleared the property or not.
177    service_test_->SetServiceProperty(
178        kWifiStub, shill::kEapCertIdProperty, base::StringValue("invalid id"));
179    profile_test_->AddService(kUserProfilePath, kWifiStub);
180
181    DBusThreadManager::Get()
182        ->GetShillManagerClient()
183        ->GetTestInterface()
184        ->AddManagerService(kWifiStub, true);
185  }
186
187  // Setup a policy with a certificate pattern that matches any client cert that
188  // is signed by the test CA cert (stored in |test_ca_cert_pem_|). In
189  // particular it will match the test client cert.
190  void SetupPolicy() {
191    const char* kTestPolicyTemplate =
192        "[ { \"GUID\": \"wifi_stub\","
193        "    \"Name\": \"wifi_stub\","
194        "    \"Type\": \"WiFi\","
195        "    \"WiFi\": {"
196        "      \"Security\": \"WPA-EAP\","
197        "      \"SSID\": \"wifi_ssid\","
198        "      \"EAP\": {"
199        "        \"Outer\": \"EAP-TLS\","
200        "        \"ClientCertType\": \"Pattern\","
201        "        \"ClientCertPattern\": {"
202        "          \"IssuerCAPEMs\": [ \"%s\" ]"
203        "        }"
204        "      }"
205        "    }"
206        "} ]";
207    std::string policy_json =
208        base::StringPrintf(kTestPolicyTemplate, test_ca_cert_pem_.c_str());
209
210    std::string error;
211    scoped_ptr<base::Value> policy_value(base::JSONReader::ReadAndReturnError(
212        policy_json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, &error));
213    ASSERT_TRUE(policy_value) << error;
214
215    base::ListValue* policy = NULL;
216    ASSERT_TRUE(policy_value->GetAsList(&policy));
217
218    managed_config_handler_->SetPolicy(
219        onc::ONC_SOURCE_USER_POLICY,
220        kUserHash,
221        *policy,
222        base::DictionaryValue() /* no global network config */);
223  }
224
225  void GetClientCertProperties(std::string* pkcs11_id) {
226    pkcs11_id->clear();
227    const base::DictionaryValue* properties =
228        service_test_->GetServiceProperties(kWifiStub);
229    if (!properties)
230      return;
231    properties->GetStringWithoutPathExpansion(shill::kEapCertIdProperty,
232                                              pkcs11_id);
233  }
234
235  ShillServiceClient::TestInterface* service_test_;
236  ShillProfileClient::TestInterface* profile_test_;
237  std::string test_cert_id_;
238  scoped_refptr<net::X509Certificate> test_ca_cert_;
239  std::string test_ca_cert_pem_;
240  base::MessageLoop message_loop_;
241
242 private:
243  void CleanupSlotContents() {
244    CERTCertList* cert_list = PK11_ListCertsInSlot(private_slot_.get());
245    for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
246         !CERT_LIST_END(node, cert_list);
247         node = CERT_LIST_NEXT(node)) {
248      scoped_refptr<net::X509Certificate> cert(
249          net::X509Certificate::CreateFromHandle(
250              node->cert, net::X509Certificate::OSCertHandles()));
251      test_nssdb_->DeleteCertAndKey(cert.get());
252    }
253    CERT_DestroyCertList(cert_list);
254  }
255
256  CertLoader* cert_loader_;
257  scoped_refptr<net::X509Certificate> test_client_cert_;
258  scoped_ptr<NetworkStateHandler> network_state_handler_;
259  scoped_ptr<NetworkProfileHandler> network_profile_handler_;
260  scoped_ptr<NetworkConfigurationHandler> network_config_handler_;
261  scoped_ptr<ManagedNetworkConfigurationHandlerImpl> managed_config_handler_;
262  scoped_ptr<ClientCertResolver> client_cert_resolver_;
263  crypto::ScopedTestNSSChromeOSUser user_;
264  scoped_ptr<net::NSSCertDatabaseChromeOS> test_nssdb_;
265  crypto::ScopedPK11Slot private_slot_;
266
267  DISALLOW_COPY_AND_ASSIGN(ClientCertResolverTest);
268};
269
270TEST_F(ClientCertResolverTest, NoMatchingCertificates) {
271  SetupNetworkHandlers();
272  SetupWifi();
273  StartCertLoader();
274  SetupPolicy();
275  base::RunLoop().RunUntilIdle();
276
277  // Verify that no client certificate was configured.
278  std::string pkcs11_id;
279  GetClientCertProperties(&pkcs11_id);
280  EXPECT_EQ(std::string(), pkcs11_id);
281}
282
283TEST_F(ClientCertResolverTest, ResolveOnCertificatesLoaded) {
284  SetupNetworkHandlers();
285  SetupWifi();
286  SetupTestCerts();
287  SetupPolicy();
288  base::RunLoop().RunUntilIdle();
289
290  StartCertLoader();
291  base::RunLoop().RunUntilIdle();
292
293  // Verify that the resolver positively matched the pattern in the policy with
294  // the test client cert and configured the network.
295  std::string pkcs11_id;
296  GetClientCertProperties(&pkcs11_id);
297  EXPECT_EQ(test_cert_id_, pkcs11_id);
298}
299
300TEST_F(ClientCertResolverTest, ResolveAfterPolicyApplication) {
301  SetupTestCerts();
302  StartCertLoader();
303  SetupNetworkHandlers();
304  SetupWifi();
305  base::RunLoop().RunUntilIdle();
306
307  // Policy application will trigger the ClientCertResolver.
308  SetupPolicy();
309  base::RunLoop().RunUntilIdle();
310
311  // Verify that the resolver positively matched the pattern in the policy with
312  // the test client cert and configured the network.
313  std::string pkcs11_id;
314  GetClientCertProperties(&pkcs11_id);
315  EXPECT_EQ(test_cert_id_, pkcs11_id);
316}
317
318}  // namespace chromeos
319