Home | History | Annotate | Download | only in network
      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