client_cert_resolver_unittest.cc revision 3551c9c881056c480085172ff9840cab31610854
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(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 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