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/files/file_path.h"
     10 #include "base/files/file_util.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 
     37 namespace chromeos {
     38 
     39 namespace {
     40 
     41 const char* kWifiStub = "wifi_stub";
     42 const char* kWifiSSID = "wifi_ssid";
     43 const char* kUserProfilePath = "user_profile";
     44 const char* kUserHash = "user_hash";
     45 
     46 }  // namespace
     47 
     48 class 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::Initialize();
     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_.get()) {
    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(net::OK,
    140               test_nssdb_->ImportFromPKCS12(module.get(),
    141                                             pkcs12_data,
    142                                             base::string16(),
    143                                             false,
    144                                             &client_cert_list));
    145     ASSERT_TRUE(!client_cert_list.empty());
    146     test_client_cert_ = client_cert_list[0];
    147   }
    148 
    149   void SetupNetworkHandlers() {
    150     network_state_handler_.reset(NetworkStateHandler::InitializeForTest());
    151     network_profile_handler_.reset(new NetworkProfileHandler());
    152     network_config_handler_.reset(new NetworkConfigurationHandler());
    153     managed_config_handler_.reset(new ManagedNetworkConfigurationHandlerImpl());
    154     client_cert_resolver_.reset(new ClientCertResolver());
    155 
    156     network_profile_handler_->Init();
    157     network_config_handler_->Init(network_state_handler_.get());
    158     managed_config_handler_->Init(network_state_handler_.get(),
    159                                   network_profile_handler_.get(),
    160                                   network_config_handler_.get(),
    161                                   NULL /* network_device_handler */);
    162     client_cert_resolver_->Init(network_state_handler_.get(),
    163                                 managed_config_handler_.get());
    164     client_cert_resolver_->SetSlowTaskRunnerForTest(
    165         message_loop_.message_loop_proxy());
    166 
    167     profile_test_->AddProfile(kUserProfilePath, kUserHash);
    168   }
    169 
    170   void SetupWifi() {
    171     service_test_->SetServiceProperties(kWifiStub,
    172                                         kWifiStub,
    173                                         kWifiSSID,
    174                                         shill::kTypeWifi,
    175                                         shill::kStateOnline,
    176                                         true /* visible */);
    177     // Set an arbitrary cert id, so that we can check afterwards whether we
    178     // cleared the property or not.
    179     service_test_->SetServiceProperty(
    180         kWifiStub, shill::kEapCertIdProperty, base::StringValue("invalid id"));
    181     profile_test_->AddService(kUserProfilePath, kWifiStub);
    182 
    183     DBusThreadManager::Get()
    184         ->GetShillManagerClient()
    185         ->GetTestInterface()
    186         ->AddManagerService(kWifiStub, true);
    187   }
    188 
    189   // Setup a policy with a certificate pattern that matches any client cert that
    190   // is signed by the test CA cert (stored in |test_ca_cert_pem_|). In
    191   // particular it will match the test client cert.
    192   void SetupPolicy() {
    193     const char* kTestPolicyTemplate =
    194         "[ { \"GUID\": \"wifi_stub\","
    195         "    \"Name\": \"wifi_stub\","
    196         "    \"Type\": \"WiFi\","
    197         "    \"WiFi\": {"
    198         "      \"Security\": \"WPA-EAP\","
    199         "      \"SSID\": \"wifi_ssid\","
    200         "      \"EAP\": {"
    201         "        \"Outer\": \"EAP-TLS\","
    202         "        \"ClientCertType\": \"Pattern\","
    203         "        \"ClientCertPattern\": {"
    204         "          \"IssuerCAPEMs\": [ \"%s\" ]"
    205         "        }"
    206         "      }"
    207         "    }"
    208         "} ]";
    209     std::string policy_json =
    210         base::StringPrintf(kTestPolicyTemplate, test_ca_cert_pem_.c_str());
    211 
    212     std::string error;
    213     scoped_ptr<base::Value> policy_value(base::JSONReader::ReadAndReturnError(
    214         policy_json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, &error));
    215     ASSERT_TRUE(policy_value) << error;
    216 
    217     base::ListValue* policy = NULL;
    218     ASSERT_TRUE(policy_value->GetAsList(&policy));
    219 
    220     managed_config_handler_->SetPolicy(
    221         onc::ONC_SOURCE_USER_POLICY,
    222         kUserHash,
    223         *policy,
    224         base::DictionaryValue() /* no global network config */);
    225   }
    226 
    227   void GetClientCertProperties(std::string* pkcs11_id) {
    228     pkcs11_id->clear();
    229     const base::DictionaryValue* properties =
    230         service_test_->GetServiceProperties(kWifiStub);
    231     if (!properties)
    232       return;
    233     properties->GetStringWithoutPathExpansion(shill::kEapCertIdProperty,
    234                                               pkcs11_id);
    235   }
    236 
    237   ShillServiceClient::TestInterface* service_test_;
    238   ShillProfileClient::TestInterface* profile_test_;
    239   std::string test_cert_id_;
    240   scoped_refptr<net::X509Certificate> test_ca_cert_;
    241   std::string test_ca_cert_pem_;
    242   base::MessageLoop message_loop_;
    243 
    244  private:
    245   void CleanupSlotContents() {
    246     CERTCertList* cert_list = PK11_ListCertsInSlot(private_slot_.get());
    247     for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
    248          !CERT_LIST_END(node, cert_list);
    249          node = CERT_LIST_NEXT(node)) {
    250       scoped_refptr<net::X509Certificate> cert(
    251           net::X509Certificate::CreateFromHandle(
    252               node->cert, net::X509Certificate::OSCertHandles()));
    253       test_nssdb_->DeleteCertAndKey(cert.get());
    254     }
    255     CERT_DestroyCertList(cert_list);
    256   }
    257 
    258   CertLoader* cert_loader_;
    259   scoped_refptr<net::X509Certificate> test_client_cert_;
    260   scoped_ptr<NetworkStateHandler> network_state_handler_;
    261   scoped_ptr<NetworkProfileHandler> network_profile_handler_;
    262   scoped_ptr<NetworkConfigurationHandler> network_config_handler_;
    263   scoped_ptr<ManagedNetworkConfigurationHandlerImpl> managed_config_handler_;
    264   scoped_ptr<ClientCertResolver> client_cert_resolver_;
    265   crypto::ScopedTestNSSChromeOSUser user_;
    266   scoped_ptr<net::NSSCertDatabaseChromeOS> test_nssdb_;
    267   crypto::ScopedPK11Slot private_slot_;
    268 
    269   DISALLOW_COPY_AND_ASSIGN(ClientCertResolverTest);
    270 };
    271 
    272 TEST_F(ClientCertResolverTest, NoMatchingCertificates) {
    273   SetupNetworkHandlers();
    274   SetupWifi();
    275   StartCertLoader();
    276   SetupPolicy();
    277   base::RunLoop().RunUntilIdle();
    278 
    279   // Verify that no client certificate was configured.
    280   std::string pkcs11_id;
    281   GetClientCertProperties(&pkcs11_id);
    282   EXPECT_EQ(std::string(), pkcs11_id);
    283 }
    284 
    285 TEST_F(ClientCertResolverTest, ResolveOnCertificatesLoaded) {
    286   SetupNetworkHandlers();
    287   SetupWifi();
    288   SetupTestCerts();
    289   SetupPolicy();
    290   base::RunLoop().RunUntilIdle();
    291 
    292   StartCertLoader();
    293   base::RunLoop().RunUntilIdle();
    294 
    295   // Verify that the resolver positively matched the pattern in the policy with
    296   // the test client cert and configured the network.
    297   std::string pkcs11_id;
    298   GetClientCertProperties(&pkcs11_id);
    299   EXPECT_EQ(test_cert_id_, pkcs11_id);
    300 }
    301 
    302 TEST_F(ClientCertResolverTest, ResolveAfterPolicyApplication) {
    303   SetupTestCerts();
    304   StartCertLoader();
    305   SetupNetworkHandlers();
    306   SetupWifi();
    307   base::RunLoop().RunUntilIdle();
    308 
    309   // Policy application will trigger the ClientCertResolver.
    310   SetupPolicy();
    311   base::RunLoop().RunUntilIdle();
    312 
    313   // Verify that the resolver positively matched the pattern in the policy with
    314   // the test client cert and configured the network.
    315   std::string pkcs11_id;
    316   GetClientCertProperties(&pkcs11_id);
    317   EXPECT_EQ(test_cert_id_, pkcs11_id);
    318 }
    319 
    320 }  // namespace chromeos
    321