summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortbarzic@chromium.org <tbarzic@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-21 23:14:53 +0000
committertbarzic@chromium.org <tbarzic@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-21 23:14:53 +0000
commitb2390837986574afa95926297767fad126bc9b74 (patch)
tree8744896918af941c5850db968d7f71bf428dfea4
parentb2d1f2dcf6d9181cd8b60804e6cbf0c13fbf9a93 (diff)
downloadchromium_src-b2390837986574afa95926297767fad126bc9b74.zip
chromium_src-b2390837986574afa95926297767fad126bc9b74.tar.gz
chromium_src-b2390837986574afa95926297767fad126bc9b74.tar.bz2
Split chromeos::CertLoader
CertLoader does both TPM token initialization and certificate loading from the cert database. This extracts token initializetion in a separate class. BUG=315343 Review URL: https://codereview.chromium.org/132313004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@246154 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/chrome_browser_main_chromeos.cc7
-rw-r--r--chrome/browser/chromeos/login/parallel_authenticator_unittest.cc10
-rw-r--r--chrome/browser/chromeos/options/vpn_config_view.cc1
-rw-r--r--chrome/browser/chromeos/options/wifi_config_view.cc1
-rw-r--r--chrome/browser/chromeos/options/wimax_config_view.cc1
-rw-r--r--chrome/browser/chromeos/settings/device_settings_service.cc35
-rw-r--r--chrome/browser/chromeos/settings/device_settings_service.h23
-rw-r--r--chrome/browser/chromeos/settings/device_settings_service_unittest.cc16
-rw-r--r--chromeos/cert_loader.cc254
-rw-r--r--chromeos/cert_loader.h133
-rw-r--r--chromeos/chromeos.gyp2
-rw-r--r--chromeos/network/client_cert_resolver.cc2
-rw-r--r--chromeos/network/client_cert_resolver_unittest.cc14
-rw-r--r--chromeos/network/network_cert_migrator.cc1
-rw-r--r--chromeos/network/network_cert_migrator_unittest.cc9
-rw-r--r--chromeos/network/network_connection_handler.cc1
-rw-r--r--chromeos/tpm_token_loader.cc289
-rw-r--r--chromeos/tpm_token_loader.h147
18 files changed, 576 insertions, 370 deletions
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index feadf19..b995ba0 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -77,6 +77,7 @@
#include "chrome/common/pref_names.h"
#include "chromeos/audio/audio_devices_pref_handler.h"
#include "chromeos/audio/cras_audio_handler.h"
+#include "chromeos/cert_loader.h"
#include "chromeos/chromeos_paths.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/cryptohome/async_method_caller.h"
@@ -93,6 +94,7 @@
#include "chromeos/network/network_handler.h"
#include "chromeos/power/power_data_collector.h"
#include "chromeos/system/statistics_provider.h"
+#include "chromeos/tpm_token_loader.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/power_save_blocker.h"
@@ -266,6 +268,7 @@ class DBusServices {
LoginState::Initialize();
SystemSaltGetter::Initialize();
+ TPMTokenLoader::Initialize();
CertLoader::Initialize();
// This function and SystemKeyEventListener use InputMethodManager.
@@ -314,6 +317,8 @@ class DBusServices {
SystemSaltGetter::Shutdown();
LoginState::Shutdown();
+ CertLoader::Shutdown();
+ TPMTokenLoader::Shutdown();
CrosDBusService::Shutdown();
@@ -410,7 +415,7 @@ void ChromeBrowserMainPartsChromeos::PostMainMessageLoopStart() {
// about_flags settings are applied in ChromeBrowserMainParts::PreCreateThreads.
void ChromeBrowserMainPartsChromeos::PreMainMessageLoopRun() {
// Set the crypto thread after the IO thread has been created/started.
- CertLoader::Get()->SetCryptoTaskRunner(
+ TPMTokenLoader::Get()->SetCryptoTaskRunner(
content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::IO));
diff --git a/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc b/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc
index 1a61dc2..08f02bd 100644
--- a/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc
+++ b/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc
@@ -317,11 +317,11 @@ TEST_F(ParallelAuthenticatorTest, ResolveOwnerNeededFailedMount) {
SetAndResolveState(auth_.get(), state_.release()));
EXPECT_TRUE(LoginState::Get()->IsInSafeMode());
- // Simulate certificates load event. The exact certificates loaded are not
- // actually used by the DeviceSettingsService, so it is OK to pass an empty
- // list.
- DeviceSettingsService::Get()->OnCertificatesLoaded(net::CertificateList(),
- true);
+ // Simulate TPM token ready event. The tpm token parameters are not
+ // actually used by the DeviceSettingsService, so it is OK to pass arbitrary
+ // values.
+ DeviceSettingsService::Get()->OnTPMTokenReady("pin", "token_name", 0);
+
// Flush all the pending operations. The operations should induce an owner
// verification.
device_settings_test_helper_.Flush();
diff --git a/chrome/browser/chromeos/options/vpn_config_view.cc b/chrome/browser/chromeos/options/vpn_config_view.cc
index e737b9d..3c1ffae 100644
--- a/chrome/browser/chromeos/options/vpn_config_view.cc
+++ b/chrome/browser/chromeos/options/vpn_config_view.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/chromeos/options/vpn_config_view.h"
#include "ash/system/chromeos/network/network_connect.h"
+#include "base/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
diff --git a/chrome/browser/chromeos/options/wifi_config_view.cc b/chrome/browser/chromeos/options/wifi_config_view.cc
index 412954a..2f749d0 100644
--- a/chrome/browser/chromeos/options/wifi_config_view.cc
+++ b/chrome/browser/chromeos/options/wifi_config_view.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/chromeos/options/wifi_config_view.h"
#include "ash/system/chromeos/network/network_connect.h"
+#include "base/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
diff --git a/chrome/browser/chromeos/options/wimax_config_view.cc b/chrome/browser/chromeos/options/wimax_config_view.cc
index 2fe342f..2b4eff8 100644
--- a/chrome/browser/chromeos/options/wimax_config_view.cc
+++ b/chrome/browser/chromeos/options/wimax_config_view.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/chromeos/options/wimax_config_view.h"
#include "ash/system/chromeos/network/network_connect.h"
+#include "base/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
diff --git a/chrome/browser/chromeos/settings/device_settings_service.cc b/chrome/browser/chromeos/settings/device_settings_service.cc
index 1173a87..5aa7efe 100644
--- a/chrome/browser/chromeos/settings/device_settings_service.cc
+++ b/chrome/browser/chromeos/settings/device_settings_service.cc
@@ -72,21 +72,21 @@ DeviceSettingsService* DeviceSettingsService::Get() {
DeviceSettingsService::DeviceSettingsService()
: session_manager_client_(NULL),
- weak_factory_(this),
store_status_(STORE_SUCCESS),
- certificates_loaded_(false),
- owner_key_loaded_with_certificates_(false),
- load_retries_left_(kMaxLoadRetries) {
- if (CertLoader::IsInitialized()) {
- certificates_loaded_ = CertLoader::Get()->certificates_loaded();
- CertLoader::Get()->AddObserver(this);
+ waiting_for_tpm_token_(true),
+ owner_key_loaded_with_tpm_token_(false),
+ load_retries_left_(kMaxLoadRetries),
+ weak_factory_(this) {
+ if (TPMTokenLoader::IsInitialized()) {
+ waiting_for_tpm_token_ = !TPMTokenLoader::Get()->IsTPMTokenReady();
+ TPMTokenLoader::Get()->AddObserver(this);
}
}
DeviceSettingsService::~DeviceSettingsService() {
DCHECK(pending_operations_.empty());
- if (CertLoader::IsInitialized())
- CertLoader::Get()->RemoveObserver(this);
+ if (TPMTokenLoader::IsInitialized())
+ TPMTokenLoader::Get()->RemoveObserver(this);
}
void DeviceSettingsService::SetSessionManager(
@@ -179,7 +179,7 @@ bool DeviceSettingsService::HasPrivateOwnerKey() {
void DeviceSettingsService::IsCurrentUserOwnerAsync(
const IsCurrentUserOwnerCallback& callback) {
- if (owner_key_loaded_with_certificates_) {
+ if (owner_key_loaded_with_tpm_token_) {
// If the current owner key was loaded while the certificates were loaded,
// or the certificate loader is not initialized, in which case the private
// key cannot be set, report status immediately.
@@ -235,11 +235,12 @@ void DeviceSettingsService::PropertyChangeComplete(bool success) {
EnsureReload(false);
}
-void DeviceSettingsService::OnCertificatesLoaded(
- const net::CertificateList& cert_list,
- bool initial_load) {
- certificates_loaded_ = true;
- // CertLoader initializes the TPM and NSS database which is necessary to
+void DeviceSettingsService::OnTPMTokenReady(const std::string& tpm_user_pin,
+ const std::string& tpm_token_name,
+ int tpm_token_slot_id) {
+ waiting_for_tpm_token_ = false;
+
+ // TPMTokenLoader initializes the TPM and NSS database which is necessary to
// determine ownership. Force a reload once we know these are initialized.
EnsureReload(true);
}
@@ -338,8 +339,8 @@ void DeviceSettingsService::HandleCompletedOperation(
iter->Run(ownership_status);
}
- if (certificates_loaded_) {
- owner_key_loaded_with_certificates_ = true;
+ if (!waiting_for_tpm_token_) {
+ owner_key_loaded_with_tpm_token_ = true;
std::vector<IsCurrentUserOwnerCallback> is_owner_callbacks;
is_owner_callbacks.swap(pending_is_current_user_owner_callbacks_);
for (std::vector<IsCurrentUserOwnerCallback>::iterator iter(
diff --git a/chrome/browser/chromeos/settings/device_settings_service.h b/chrome/browser/chromeos/settings/device_settings_service.h
index 1d66569..b70a753 100644
--- a/chrome/browser/chromeos/settings/device_settings_service.h
+++ b/chrome/browser/chromeos/settings/device_settings_service.h
@@ -15,8 +15,8 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
-#include "chromeos/cert_loader.h"
#include "chromeos/dbus/session_manager_client.h"
+#include "chromeos/tpm_token_loader.h"
#include "components/policy/core/common/cloud/cloud_policy_validator.h"
namespace crypto {
@@ -73,7 +73,7 @@ class OwnerKey : public base::RefCountedThreadSafe<OwnerKey> {
// DeviceSettingsService generates notifications for key and policy update
// events so interested parties can reload state as appropriate.
class DeviceSettingsService : public SessionManagerClient::Observer,
- public CertLoader::Observer {
+ public TPMTokenLoader::Observer {
public:
// Indicates ownership status of the device.
enum OwnershipStatus {
@@ -196,9 +196,10 @@ class DeviceSettingsService : public SessionManagerClient::Observer,
virtual void OwnerKeySet(bool success) OVERRIDE;
virtual void PropertyChangeComplete(bool success) OVERRIDE;
- // CertLoader::Observer:
- virtual void OnCertificatesLoaded(const net::CertificateList& cert_list,
- bool initial_load) OVERRIDE;
+ // TPMTokenLoader::Observer:
+ virtual void OnTPMTokenReady(const std::string& tpm_user_pin,
+ const std::string& tpm_token_name,
+ int tpm_token_slot_id) OVERRIDE;
private:
// Enqueues a new operation. Takes ownership of |operation| and starts it
@@ -224,8 +225,6 @@ class DeviceSettingsService : public SessionManagerClient::Observer,
SessionManagerClient* session_manager_client_;
scoped_refptr<OwnerKeyUtil> owner_key_util_;
- base::WeakPtrFactory<DeviceSettingsService> weak_factory_;
-
Status store_status_;
std::vector<OwnershipStatusCallback> pending_ownership_status_callbacks_;
@@ -234,11 +233,11 @@ class DeviceSettingsService : public SessionManagerClient::Observer,
std::string username_;
scoped_refptr<OwnerKey> owner_key_;
- // Whether certificates have been loaded by CertLoader.
- bool certificates_loaded_;
- // Whether certificates were loaded when the current owner key was set.
+ // Whether TPM token still needs to be initialized.
+ bool waiting_for_tpm_token_;
+ // Whether TPM token was ready when the current owner key was set.
// Implies that the current user is owner iff the private owner key is set.
- bool owner_key_loaded_with_certificates_;
+ bool owner_key_loaded_with_tpm_token_;
scoped_ptr<enterprise_management::PolicyData> policy_data_;
scoped_ptr<enterprise_management::ChromeDeviceSettingsProto> device_settings_;
@@ -252,6 +251,8 @@ class DeviceSettingsService : public SessionManagerClient::Observer,
// For recoverable load errors how many retries are left before we give up.
int load_retries_left_;
+ base::WeakPtrFactory<DeviceSettingsService> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(DeviceSettingsService);
};
diff --git a/chrome/browser/chromeos/settings/device_settings_service_unittest.cc b/chrome/browser/chromeos/settings/device_settings_service_unittest.cc
index 2bcc1c0..aaa3cea 100644
--- a/chrome/browser/chromeos/settings/device_settings_service_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_settings_service_unittest.cc
@@ -323,7 +323,7 @@ TEST_F(DeviceSettingsServiceTest, OwnershipStatus) {
EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN, ownership_status_);
}
-TEST_F(DeviceSettingsServiceTest, OnCertificatesLoadedForNonOwner) {
+TEST_F(DeviceSettingsServiceTest, OnTPMTokenReadyForNonOwner) {
owner_key_util_->Clear();
EXPECT_FALSE(device_settings_service_.HasPrivateOwnerKey());
@@ -349,9 +349,7 @@ TEST_F(DeviceSettingsServiceTest, OnCertificatesLoadedForNonOwner) {
device_settings_service_.GetOwnershipStatus());
EXPECT_FALSE(is_owner_set_);
- // Simulate CertLoader reporting a new set of certificates. The passed
- // certificates are ignored.
- device_settings_service_.OnCertificatesLoaded(net::CertificateList(), true);
+ device_settings_service_.OnTPMTokenReady("tpm_pin", "tpm_token", 0);
FlushDeviceSettings();
EXPECT_FALSE(device_settings_service_.HasPrivateOwnerKey());
@@ -366,7 +364,7 @@ TEST_F(DeviceSettingsServiceTest, OnCertificatesLoadedForNonOwner) {
EXPECT_FALSE(is_owner_);
}
-TEST_F(DeviceSettingsServiceTest, OnCertificatesLoadedForOwner) {
+TEST_F(DeviceSettingsServiceTest, OnTPMTokenReadyForOwner) {
owner_key_util_->Clear();
EXPECT_FALSE(device_settings_service_.HasPrivateOwnerKey());
@@ -394,9 +392,7 @@ TEST_F(DeviceSettingsServiceTest, OnCertificatesLoadedForOwner) {
owner_key_util_->SetPrivateKey(device_policy_.GetSigningKey());
device_settings_service_.SetUsername(device_policy_.policy_data().username());
- // Simulate CertLoader reporting a new set of certificates. The passed
- // certificates are ignored.
- device_settings_service_.OnCertificatesLoaded(net::CertificateList(), true);
+ device_settings_service_.OnTPMTokenReady("tpm_pin", "tpm_token_name", 0);
FlushDeviceSettings();
EXPECT_TRUE(device_settings_service_.HasPrivateOwnerKey());
@@ -424,9 +420,7 @@ TEST_F(DeviceSettingsServiceTest, IsCurrentUserOwnerAsyncWithLoadedCerts) {
device_settings_service_.SetUsername(device_policy_.policy_data().username());
ReloadDeviceSettings();
- // Simulate CertLoader reporting a new set of certificates. The passed
- // certificates are ignored.
- device_settings_service_.OnCertificatesLoaded(net::CertificateList(), true);
+ device_settings_service_.OnTPMTokenReady("tpm_pin", "tpm_token_name", 0);
FlushDeviceSettings();
EXPECT_TRUE(device_settings_service_.HasPrivateOwnerKey());
diff --git a/chromeos/cert_loader.cc b/chromeos/cert_loader.cc
index 8b10a9e..70e4981 100644
--- a/chromeos/cert_loader.cc
+++ b/chromeos/cert_loader.cc
@@ -6,55 +6,23 @@
#include <algorithm>
-#include "base/message_loop/message_loop_proxy.h"
-#include "base/observer_list.h"
-#include "base/sequenced_task_runner.h"
+#include "base/bind.h"
+#include "base/location.h"
#include "base/strings/string_number_conversions.h"
-#include "base/sys_info.h"
#include "base/task_runner_util.h"
#include "base/threading/worker_pool.h"
-#include "chromeos/dbus/cryptohome_client.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "crypto/encryptor.h"
#include "crypto/nss_util.h"
-#include "crypto/sha2.h"
-#include "crypto/symmetric_key.h"
#include "net/cert/nss_cert_database.h"
+#include "net/cert/x509_certificate.h"
namespace chromeos {
namespace {
-const int64 kInitialRequestDelayMs = 100;
-const int64 kMaxRequestDelayMs = 300000; // 5 minutes
-
-// Calculates the delay before running next attempt to initiatialize the TPM
-// token, if |last_delay| was the last or initial delay.
-base::TimeDelta GetNextRequestDelayMs(base::TimeDelta last_delay) {
- // This implements an exponential backoff, as we don't know in which order of
- // magnitude the TPM token changes it's state.
- base::TimeDelta next_delay = last_delay * 2;
-
- // Cap the delay to prevent an overflow. This threshold is arbitrarily chosen.
- const base::TimeDelta max_delay =
- base::TimeDelta::FromMilliseconds(kMaxRequestDelayMs);
- if (next_delay > max_delay)
- next_delay = max_delay;
- return next_delay;
-}
-
-void LoadNSSCertificates(net::CertificateList* cert_list) {
- net::NSSCertDatabase::GetInstance()->ListCerts(cert_list);
-}
-
-void CallOpenPersistentNSSDB() {
- // Called from crypto_task_runner_.
- VLOG(1) << "CallOpenPersistentNSSDB";
-
- // Ensure we've opened the user's key/certificate database.
- if (base::SysInfo::IsRunningOnChromeOS())
- crypto::OpenPersistentNSSDB();
- crypto::EnableTPMTokenForNSS();
+// Loads certificates from |cert_database| into |cert_list|.
+void LoadNSSCertificates(net::NSSCertDatabase* cert_database,
+ net::CertificateList* cert_list) {
+ cert_database->ListCerts(cert_list);
}
} // namespace
@@ -86,29 +54,14 @@ bool CertLoader::IsInitialized() {
}
CertLoader::CertLoader()
- : initialize_tpm_for_test_(false),
- certificates_requested_(false),
+ : certificates_requested_(false),
certificates_loaded_(false),
certificates_update_required_(false),
certificates_update_running_(false),
- tpm_token_state_(TPM_STATE_UNKNOWN),
- tpm_request_delay_(
- base::TimeDelta::FromMilliseconds(kInitialRequestDelayMs)),
tpm_token_slot_id_(-1),
- initialize_token_factory_(this),
- update_certificates_factory_(this) {
- if (LoginState::IsInitialized())
- LoginState::Get()->AddObserver(this);
-}
-
-void CertLoader::InitializeTPMForTest() {
- initialize_tpm_for_test_ = true;
-}
-
-void CertLoader::SetCryptoTaskRunner(
- const scoped_refptr<base::SequencedTaskRunner>& crypto_task_runner) {
- crypto_task_runner_ = crypto_task_runner;
- MaybeRequestCertificates();
+ weak_factory_(this) {
+ if (TPMTokenLoader::IsInitialized())
+ TPMTokenLoader::Get()->AddObserver(this);
}
void CertLoader::SetSlowTaskRunnerForTest(
@@ -118,8 +71,8 @@ void CertLoader::SetSlowTaskRunnerForTest(
CertLoader::~CertLoader() {
net::CertDatabase::GetInstance()->RemoveObserver(this);
- if (LoginState::IsInitialized())
- LoginState::Get()->RemoveObserver(this);
+ if (TPMTokenLoader::IsInitialized())
+ TPMTokenLoader::Get()->RemoveObserver(this);
}
void CertLoader::AddObserver(CertLoader::Observer* observer) {
@@ -130,115 +83,12 @@ void CertLoader::RemoveObserver(CertLoader::Observer* observer) {
observers_.RemoveObserver(observer);
}
-bool CertLoader::CertificatesLoading() const {
- return certificates_requested_ && !certificates_loaded_;
-}
-
bool CertLoader::IsHardwareBacked() const {
return !tpm_token_name_.empty();
}
-void CertLoader::MaybeRequestCertificates() {
- CHECK(thread_checker_.CalledOnValidThread());
-
- // This is the entry point to the TPM token initialization process,
- // which we should do at most once.
- if (certificates_requested_ || !crypto_task_runner_.get())
- return;
-
- if (!LoginState::IsInitialized())
- return;
-
- bool request_certificates = LoginState::Get()->IsUserLoggedIn() ||
- LoginState::Get()->IsInSafeMode();
-
- VLOG(1) << "RequestCertificates: " << request_certificates;
- if (!request_certificates)
- return;
-
- certificates_requested_ = true;
-
- // Ensure we only initialize the TPM token once.
- DCHECK_EQ(tpm_token_state_, TPM_STATE_UNKNOWN);
- if (!initialize_tpm_for_test_ && !base::SysInfo::IsRunningOnChromeOS())
- tpm_token_state_ = TPM_DISABLED;
-
- // Treat TPM as disabled for guest users since they do not store certs.
- if (LoginState::Get()->IsGuestUser())
- tpm_token_state_ = TPM_DISABLED;
-
- InitializeTokenAndLoadCertificates();
-}
-
-void CertLoader::InitializeTokenAndLoadCertificates() {
- CHECK(thread_checker_.CalledOnValidThread());
- VLOG(1) << "InitializeTokenAndLoadCertificates: " << tpm_token_state_;
-
- switch (tpm_token_state_) {
- case TPM_STATE_UNKNOWN: {
- crypto_task_runner_->PostTaskAndReply(
- FROM_HERE,
- base::Bind(&CallOpenPersistentNSSDB),
- base::Bind(&CertLoader::OnPersistentNSSDBOpened,
- initialize_token_factory_.GetWeakPtr()));
- return;
- }
- case TPM_DB_OPENED: {
- DBusThreadManager::Get()->GetCryptohomeClient()->TpmIsEnabled(
- base::Bind(&CertLoader::OnTpmIsEnabled,
- initialize_token_factory_.GetWeakPtr()));
- return;
- }
- case TPM_DISABLED: {
- // TPM is disabled, so proceed with empty tpm token name.
- StartLoadCertificates();
- return;
- }
- case TPM_ENABLED: {
- DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11IsTpmTokenReady(
- base::Bind(&CertLoader::OnPkcs11IsTpmTokenReady,
- initialize_token_factory_.GetWeakPtr()));
- return;
- }
- case TPM_TOKEN_READY: {
- // Retrieve token_name_ and user_pin_ here since they will never change
- // and CryptohomeClient calls are not thread safe.
- DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11GetTpmTokenInfo(
- base::Bind(&CertLoader::OnPkcs11GetTpmTokenInfo,
- initialize_token_factory_.GetWeakPtr()));
- return;
- }
- case TPM_TOKEN_INFO_RECEIVED: {
- base::PostTaskAndReplyWithResult(
- crypto_task_runner_.get(),
- FROM_HERE,
- base::Bind(&crypto::InitializeTPMToken, tpm_token_slot_id_),
- base::Bind(&CertLoader::OnTPMTokenInitialized,
- initialize_token_factory_.GetWeakPtr()));
- return;
- }
- case TPM_TOKEN_INITIALIZED: {
- StartLoadCertificates();
- return;
- }
- }
-}
-
-void CertLoader::RetryTokenInitializationLater() {
- CHECK(thread_checker_.CalledOnValidThread());
- LOG(WARNING) << "Retry token initialization later.";
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&CertLoader::InitializeTokenAndLoadCertificates,
- initialize_token_factory_.GetWeakPtr()),
- tpm_request_delay_);
- tpm_request_delay_ = GetNextRequestDelayMs(tpm_request_delay_);
-}
-
-void CertLoader::OnPersistentNSSDBOpened() {
- VLOG(1) << "PersistentNSSDBOpened";
- tpm_token_state_ = TPM_DB_OPENED;
- InitializeTokenAndLoadCertificates();
+bool CertLoader::CertificatesLoading() const {
+ return certificates_requested_ && !certificates_loaded_;
}
// This is copied from chrome/common/net/x509_certificate_model_nss.cc.
@@ -270,61 +120,11 @@ std::string CertLoader::GetPkcs11IdForCert(const net::X509Certificate& cert) {
return pkcs11_id;
}
-void CertLoader::OnTpmIsEnabled(DBusMethodCallStatus call_status,
- bool tpm_is_enabled) {
- VLOG(1) << "OnTpmIsEnabled: " << tpm_is_enabled;
-
- if (call_status == DBUS_METHOD_CALL_SUCCESS && tpm_is_enabled)
- tpm_token_state_ = TPM_ENABLED;
- else
- tpm_token_state_ = TPM_DISABLED;
-
- InitializeTokenAndLoadCertificates();
-}
-
-void CertLoader::OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status,
- bool is_tpm_token_ready) {
- VLOG(1) << "OnPkcs11IsTpmTokenReady: " << is_tpm_token_ready;
-
- if (call_status == DBUS_METHOD_CALL_FAILURE || !is_tpm_token_ready) {
- RetryTokenInitializationLater();
+void CertLoader::RequestCertificates() {
+ if (certificates_requested_)
return;
- }
-
- tpm_token_state_ = TPM_TOKEN_READY;
- InitializeTokenAndLoadCertificates();
-}
-
-void CertLoader::OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status,
- const std::string& token_name,
- const std::string& user_pin,
- int token_slot_id) {
- VLOG(1) << "OnPkcs11GetTpmTokenInfo: " << token_name;
-
- if (call_status == DBUS_METHOD_CALL_FAILURE) {
- RetryTokenInitializationLater();
- return;
- }
-
- tpm_token_name_ = token_name;
- tpm_token_slot_id_ = token_slot_id;
- tpm_user_pin_ = user_pin;
- tpm_token_state_ = TPM_TOKEN_INFO_RECEIVED;
-
- InitializeTokenAndLoadCertificates();
-}
-
-void CertLoader::OnTPMTokenInitialized(bool success) {
- VLOG(1) << "OnTPMTokenInitialized: " << success;
- if (!success) {
- RetryTokenInitializationLater();
- return;
- }
- tpm_token_state_ = TPM_TOKEN_INITIALIZED;
- InitializeTokenAndLoadCertificates();
-}
+ certificates_requested_ = true;
-void CertLoader::StartLoadCertificates() {
DCHECK(!certificates_loaded_ && !certificates_update_running_);
net::CertDatabase::GetInstance()->AddObserver(this);
LoadCertificates();
@@ -348,9 +148,11 @@ void CertLoader::LoadCertificates() {
task_runner = base::WorkerPool::GetTaskRunner(true /* task is slow */);
task_runner->PostTaskAndReply(
FROM_HERE,
- base::Bind(LoadNSSCertificates, cert_list),
+ base::Bind(LoadNSSCertificates,
+ net::NSSCertDatabase::GetInstance(),
+ cert_list),
base::Bind(&CertLoader::UpdateCertificates,
- update_certificates_factory_.GetWeakPtr(),
+ weak_factory_.GetWeakPtr(),
base::Owned(cert_list)));
}
@@ -393,9 +195,15 @@ void CertLoader::OnCertRemoved(const net::X509Certificate* cert) {
LoadCertificates();
}
-void CertLoader::LoggedInStateChanged() {
- VLOG(1) << "LoggedInStateChanged";
- MaybeRequestCertificates();
+void CertLoader::OnTPMTokenReady(const std::string& tpm_user_pin,
+ const std::string& tpm_token_name,
+ int tpm_token_slot_id) {
+ tpm_user_pin_ = tpm_user_pin;
+ tpm_token_name_ = tpm_token_name;
+ tpm_token_slot_id_ = tpm_token_slot_id;
+
+ VLOG(1) << "TPM token ready.";
+ RequestCertificates();
}
} // namespace chromeos
diff --git a/chromeos/cert_loader.h b/chromeos/cert_loader.h
index e8534be..0ce661c 100644
--- a/chromeos/cert_loader.h
+++ b/chromeos/cert_loader.h
@@ -9,34 +9,37 @@
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "base/observer_list_threadsafe.h"
+#include "base/observer_list.h"
#include "base/threading/thread_checker.h"
#include "chromeos/chromeos_export.h"
-#include "chromeos/dbus/dbus_method_call_status.h"
-#include "chromeos/login/login_state.h"
+#include "chromeos/tpm_token_loader.h"
#include "net/cert/cert_database.h"
-#include "net/cert/x509_certificate.h"
namespace base {
-class SequencedTaskRunner;
class TaskRunner;
}
-namespace crypto {
-class SymmetricKey;
+namespace net {
+class X509Certificate;
}
namespace chromeos {
-// This class is responsible for initializing the TPM token and loading
-// certificates once the TPM is initialized. It is expected to be constructed
-// on the UI thread and public methods should all be called from the UI thread.
-// When certificates have been loaded (after login completes), or the cert
-// database changes, observers are called with OnCertificatesLoaded().
+// This class is responsible for loading certificates once the TPM is
+// initialized. It is expected to be constructed on the UI thread and public
+// methods should all be called from the UI thread.
+// When certificates have been loaded (after login completes and tpm token is
+// initialized), or the cert database changes, observers are called with
+// OnCertificatesLoaded().
+// TODO(tbarzic): Remove direct dependency on TPMTokenLoader. The reason
+// TPMTokenLoader has to be observed is to make sure singleton NSS DB is
+// initialized before certificate loading starts. CertLoader should use
+// (primary) user specific NSS DB, whose loading already takes this into
+// account (crypto::GetPrivateSlotForChromeOSUser waits until TPM token is
+// ready).
class CHROMEOS_EXPORT CertLoader : public net::CertDatabase::Observer,
- public LoginState::Observer {
+ public TPMTokenLoader::Observer {
public:
class Observer {
public:
@@ -47,12 +50,6 @@ class CHROMEOS_EXPORT CertLoader : public net::CertDatabase::Observer,
// is called.
virtual void OnCertificatesLoaded(const net::CertificateList& cert_list,
bool initial_load) = 0;
-
- protected:
- Observer() {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Observer);
};
// Sets the global instance. Must be called before any calls to Get().
@@ -69,18 +66,6 @@ class CHROMEOS_EXPORT CertLoader : public net::CertDatabase::Observer,
static std::string GetPkcs11IdForCert(const net::X509Certificate& cert);
- // By default, CertLoader tries to load the TPMToken only if running in a
- // ChromeOS environment. Tests can call this function after Initialize() and
- // before SetCryptoTaskRunner() to enable the TPM initialization.
- void InitializeTPMForTest();
-
- // |crypto_task_runner| is the task runner that any synchronous crypto calls
- // should be made from, e.g. in Chrome this is the IO thread. Must be called
- // after the thread is started. Starts TPM initialization and Certificate
- // loading.
- void SetCryptoTaskRunner(
- const scoped_refptr<base::SequencedTaskRunner>& crypto_task_runner);
-
// Sets the task runner that any slow calls will be made from, e.g. calls
// to the NSS database. If not set, uses base::WorkerPool.
void SetSlowTaskRunnerForTest(
@@ -89,52 +74,31 @@ class CHROMEOS_EXPORT CertLoader : public net::CertDatabase::Observer,
void AddObserver(CertLoader::Observer* observer);
void RemoveObserver(CertLoader::Observer* observer);
- // Returns true when the certificate list has been requested but not loaded.
- bool CertificatesLoading() const;
-
// Returns true if the TPM is available for hardware-backed certificates.
bool IsHardwareBacked() const;
- bool certificates_loaded() const { return certificates_loaded_; }
+ // Returns true when the certificate list has been requested but not loaded.
+ bool CertificatesLoading() const;
- // TPM info is only valid once the TPM is available (IsHardwareBacked is
- // true). Otherwise empty strings will be returned.
- const std::string& tpm_token_name() const { return tpm_token_name_; }
- int tpm_token_slot_id() const { return tpm_token_slot_id_; }
- const std::string& tpm_user_pin() const { return tpm_user_pin_; }
+ bool certificates_loaded() const { return certificates_loaded_; }
// This will be empty until certificates_loaded() is true.
const net::CertificateList& cert_list() const { return cert_list_; }
+ // Getters for cached TPM token info.
+ std::string tpm_user_pin() const { return tpm_user_pin_; }
+ std::string tpm_token_name() const { return tpm_token_name_; }
+ int tpm_token_slot_id() const { return tpm_token_slot_id_; }
+
private:
CertLoader();
virtual ~CertLoader();
- void MaybeRequestCertificates();
-
- // This is the cyclic chain of callbacks to initialize the TPM token and to
- // kick off the update of the certificate list.
- void InitializeTokenAndLoadCertificates();
- void RetryTokenInitializationLater();
- void OnPersistentNSSDBOpened();
- void OnTpmIsEnabled(DBusMethodCallStatus call_status,
- bool tpm_is_enabled);
- void OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status,
- bool is_tpm_token_ready);
- void OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status,
- const std::string& token_name,
- const std::string& user_pin,
- int token_slot_id);
- void OnTPMTokenInitialized(bool success);
-
- // These calls handle the updating of the certificate list after the TPM token
- // was initialized.
-
- // Start certificate loading. Must be called at most once.
- void StartLoadCertificates();
+ // Starts certificate loading.
+ void RequestCertificates();
// Trigger a certificate load. If a certificate loading task is already in
- // progress, will start a reload once the current task finised.
+ // progress, will start a reload once the current task finished.
void LoadCertificates();
// Called if a certificate load task is finished.
@@ -147,58 +111,33 @@ class CHROMEOS_EXPORT CertLoader : public net::CertDatabase::Observer,
virtual void OnCertAdded(const net::X509Certificate* cert) OVERRIDE;
virtual void OnCertRemoved(const net::X509Certificate* cert) OVERRIDE;
- // LoginState::Observer
- virtual void LoggedInStateChanged() OVERRIDE;
-
- bool initialize_tpm_for_test_;
+ // chromeos::TPMTokenLoader::Observer
+ virtual void OnTPMTokenReady(const std::string& tpm_user_pin,
+ const std::string& tpm_token_name,
+ int tpm_token_slot_id) OVERRIDE;
ObserverList<Observer> observers_;
+ // Flags describing current CertLoader state.
bool certificates_requested_;
bool certificates_loaded_;
bool certificates_update_required_;
bool certificates_update_running_;
- // The states are traversed in this order but some might get omitted or never
- // be left.
- enum TPMTokenState {
- TPM_STATE_UNKNOWN,
- TPM_DB_OPENED,
- TPM_DISABLED,
- TPM_ENABLED,
- TPM_TOKEN_READY,
- TPM_TOKEN_INFO_RECEIVED,
- TPM_TOKEN_INITIALIZED,
- };
- TPMTokenState tpm_token_state_;
-
- // The current request delay before the next attempt to initialize the
- // TPM. Will be adapted after each attempt.
- base::TimeDelta tpm_request_delay_;
-
- // Cached TPM token info.
+ // Cached TPM token info. Set when the |OnTPMTokenReady| gets called.
+ std::string tpm_user_pin_;
std::string tpm_token_name_;
int tpm_token_slot_id_;
- std::string tpm_user_pin_;
// Cached Certificates.
net::CertificateList cert_list_;
base::ThreadChecker thread_checker_;
- // TaskRunner for crypto calls.
- scoped_refptr<base::SequencedTaskRunner> crypto_task_runner_;
-
// TaskRunner for other slow tasks. May be set in tests.
scoped_refptr<base::TaskRunner> slow_task_runner_for_test_;
- // This factory should be used only for callbacks during TPMToken
- // initialization.
- base::WeakPtrFactory<CertLoader> initialize_token_factory_;
-
- // This factory should be used only for callbacks during updating the
- // certificate list.
- base::WeakPtrFactory<CertLoader> update_certificates_factory_;
+ base::WeakPtrFactory<CertLoader> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CertLoader);
};
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp
index 14ef3f9..de1fa56 100644
--- a/chromeos/chromeos.gyp
+++ b/chromeos/chromeos.gyp
@@ -338,6 +338,8 @@
'system/name_value_pairs_parser.h',
'system/statistics_provider.cc',
'system/statistics_provider.h',
+ 'tpm_token_loader.cc',
+ 'tpm_token_loader.h'
],
'conditions': [
['use_x11 == 1', {
diff --git a/chromeos/network/client_cert_resolver.cc b/chromeos/network/client_cert_resolver.cc
index 5f686ca..018609f 100644
--- a/chromeos/network/client_cert_resolver.cc
+++ b/chromeos/network/client_cert_resolver.cc
@@ -11,6 +11,8 @@
#include <algorithm>
#include <string>
+#include "base/bind.h"
+#include "base/location.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/task_runner.h"
diff --git a/chromeos/network/client_cert_resolver_unittest.cc b/chromeos/network/client_cert_resolver_unittest.cc
index 2844de2..abcdfd4 100644
--- a/chromeos/network/client_cert_resolver_unittest.cc
+++ b/chromeos/network/client_cert_resolver_unittest.cc
@@ -12,6 +12,7 @@
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
+#include "chromeos/cert_loader.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/shill_profile_client.h"
#include "chromeos/dbus/shill_service_client.h"
@@ -20,6 +21,7 @@
#include "chromeos/network/network_configuration_handler.h"
#include "chromeos/network/network_profile_handler.h"
#include "chromeos/network/network_state_handler.h"
+#include "chromeos/tpm_token_loader.h"
#include "crypto/nss_util.h"
#include "net/base/crypto_module.h"
#include "net/base/net_errors.h"
@@ -62,11 +64,14 @@ class ClientCertResolverTest : public testing::Test {
service_test_->ClearServices();
message_loop_.RunUntilIdle();
+ TPMTokenLoader::Initialize();
+ TPMTokenLoader* tpm_token_loader = TPMTokenLoader::Get();
+ tpm_token_loader->InitializeTPMForTest();
+ tpm_token_loader->SetCryptoTaskRunner(message_loop_.message_loop_proxy());
+
CertLoader::Initialize();
- CertLoader* cert_loader = CertLoader::Get();
- cert_loader->InitializeTPMForTest();
- cert_loader->SetSlowTaskRunnerForTest(message_loop_.message_loop_proxy());
- cert_loader->SetCryptoTaskRunner(message_loop_.message_loop_proxy());
+ CertLoader::Get()->SetSlowTaskRunnerForTest(
+ message_loop_.message_loop_proxy());
}
virtual void TearDown() OVERRIDE {
@@ -76,6 +81,7 @@ class ClientCertResolverTest : public testing::Test {
network_profile_handler_.reset();
network_state_handler_.reset();
CertLoader::Shutdown();
+ TPMTokenLoader::Shutdown();
DBusThreadManager::Shutdown();
LoginState::Shutdown();
CleanupSlotContents();
diff --git a/chromeos/network/network_cert_migrator.cc b/chromeos/network/network_cert_migrator.cc
index f32dc16..693ebf5 100644
--- a/chromeos/network/network_cert_migrator.cc
+++ b/chromeos/network/network_cert_migrator.cc
@@ -7,6 +7,7 @@
#include <cert.h>
#include <string>
+#include "base/bind.h"
#include "base/location.h"
#include "base/metrics/histogram.h"
#include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/chromeos/network/network_cert_migrator_unittest.cc b/chromeos/network/network_cert_migrator_unittest.cc
index f1f5fb2..247bda5 100644
--- a/chromeos/network/network_cert_migrator_unittest.cc
+++ b/chromeos/network/network_cert_migrator_unittest.cc
@@ -9,10 +9,12 @@
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/run_loop.h"
+#include "chromeos/cert_loader.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/shill_service_client.h"
#include "chromeos/login/login_state.h"
#include "chromeos/network/network_state_handler.h"
+#include "chromeos/tpm_token_loader.h"
#include "crypto/nss_util.h"
#include "net/base/crypto_module.h"
#include "net/base/net_errors.h"
@@ -53,16 +55,21 @@ class NetworkCertMigratorTest : public testing::Test {
service_test_->ClearServices();
message_loop_.RunUntilIdle();
+ TPMTokenLoader::Initialize();
+ TPMTokenLoader* tpm_token_loader = TPMTokenLoader::Get();
+ tpm_token_loader->InitializeTPMForTest();
+ tpm_token_loader->SetCryptoTaskRunner(message_loop_.message_loop_proxy());
+
CertLoader::Initialize();
CertLoader::Get()->SetSlowTaskRunnerForTest(
message_loop_.message_loop_proxy());
- CertLoader::Get()->SetCryptoTaskRunner(message_loop_.message_loop_proxy());
}
virtual void TearDown() OVERRIDE {
network_cert_migrator_.reset();
network_state_handler_.reset();
CertLoader::Shutdown();
+ TPMTokenLoader::Shutdown();
DBusThreadManager::Shutdown();
LoginState::Shutdown();
CleanupTestCert();
diff --git a/chromeos/network/network_connection_handler.cc b/chromeos/network/network_connection_handler.cc
index eb668b3..5450338 100644
--- a/chromeos/network/network_connection_handler.cc
+++ b/chromeos/network/network_connection_handler.cc
@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/json/json_reader.h"
+#include "base/location.h"
#include "base/strings/string_number_conversions.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/chromeos/tpm_token_loader.cc b/chromeos/tpm_token_loader.cc
new file mode 100644
index 0000000..6d1bd53
--- /dev/null
+++ b/chromeos/tpm_token_loader.cc
@@ -0,0 +1,289 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/tpm_token_loader.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/sequenced_task_runner.h"
+#include "base/sys_info.h"
+#include "base/task_runner_util.h"
+#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "crypto/nss_util.h"
+
+namespace chromeos {
+
+namespace {
+
+const int64 kInitialRequestDelayMs = 100;
+const int64 kMaxRequestDelayMs = 300000; // 5 minutes
+
+// Calculates the delay before running next attempt to initiatialize the TPM
+// token, if |last_delay| was the last or initial delay.
+base::TimeDelta GetNextRequestDelayMs(base::TimeDelta last_delay) {
+ // This implements an exponential backoff, as we don't know in which order of
+ // magnitude the TPM token changes it's state.
+ base::TimeDelta next_delay = last_delay * 2;
+
+ // Cap the delay to prevent an overflow. This threshold is arbitrarily chosen.
+ const base::TimeDelta max_delay =
+ base::TimeDelta::FromMilliseconds(kMaxRequestDelayMs);
+ if (next_delay > max_delay)
+ next_delay = max_delay;
+ return next_delay;
+}
+
+void CallOpenPersistentNSSDB() {
+ // Called from crypto_task_runner_.
+ VLOG(1) << "CallOpenPersistentNSSDB";
+
+ // Ensure we've opened the user's key/certificate database.
+ if (base::SysInfo::IsRunningOnChromeOS())
+ crypto::OpenPersistentNSSDB();
+ crypto::EnableTPMTokenForNSS();
+}
+
+} // namespace
+
+static TPMTokenLoader* g_tpm_token_loader = NULL;
+
+// static
+void TPMTokenLoader::Initialize() {
+ CHECK(!g_tpm_token_loader);
+ g_tpm_token_loader = new TPMTokenLoader();
+}
+
+// static
+void TPMTokenLoader::Shutdown() {
+ CHECK(g_tpm_token_loader);
+ delete g_tpm_token_loader;
+ g_tpm_token_loader = NULL;
+}
+
+// static
+TPMTokenLoader* TPMTokenLoader::Get() {
+ CHECK(g_tpm_token_loader)
+ << "TPMTokenLoader::Get() called before Initialize()";
+ return g_tpm_token_loader;
+}
+
+// static
+bool TPMTokenLoader::IsInitialized() {
+ return g_tpm_token_loader;
+}
+
+TPMTokenLoader::TPMTokenLoader()
+ : initialize_tpm_for_test_(false),
+ tpm_token_state_(TPM_STATE_UNKNOWN),
+ tpm_request_delay_(
+ base::TimeDelta::FromMilliseconds(kInitialRequestDelayMs)),
+ tpm_token_slot_id_(-1),
+ weak_factory_(this) {
+ if (LoginState::IsInitialized())
+ LoginState::Get()->AddObserver(this);
+}
+
+void TPMTokenLoader::InitializeTPMForTest() {
+ initialize_tpm_for_test_ = true;
+}
+
+void TPMTokenLoader::SetCryptoTaskRunner(
+ const scoped_refptr<base::SequencedTaskRunner>& crypto_task_runner) {
+ crypto_task_runner_ = crypto_task_runner;
+ MaybeStartTokenInitialization();
+}
+
+TPMTokenLoader::~TPMTokenLoader() {
+ if (LoginState::IsInitialized())
+ LoginState::Get()->RemoveObserver(this);
+}
+
+void TPMTokenLoader::AddObserver(TPMTokenLoader::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void TPMTokenLoader::RemoveObserver(TPMTokenLoader::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool TPMTokenLoader::IsTPMTokenReady() const {
+ return tpm_token_state_ == TPM_DISABLED ||
+ tpm_token_state_ == TPM_TOKEN_INITIALIZED;
+}
+
+void TPMTokenLoader::MaybeStartTokenInitialization() {
+ CHECK(thread_checker_.CalledOnValidThread());
+
+ // This is the entry point to the TPM token initialization process,
+ // which we should do at most once.
+ if (tpm_token_state_ != TPM_STATE_UNKNOWN || !crypto_task_runner_.get())
+ return;
+
+ if (!LoginState::IsInitialized())
+ return;
+
+ bool request_certificates = LoginState::Get()->IsUserLoggedIn() ||
+ LoginState::Get()->IsInSafeMode();
+
+ VLOG(1) << "RequestCertificates: " << request_certificates;
+ if (!request_certificates)
+ return;
+
+ if (!initialize_tpm_for_test_ && !base::SysInfo::IsRunningOnChromeOS())
+ tpm_token_state_ = TPM_DISABLED;
+
+ // Treat TPM as disabled for guest users since they do not store certs.
+ if (LoginState::Get()->IsGuestUser())
+ tpm_token_state_ = TPM_DISABLED;
+
+ ContinueTokenInitialization();
+
+ DCHECK_NE(tpm_token_state_, TPM_STATE_UNKNOWN);
+}
+
+void TPMTokenLoader::ContinueTokenInitialization() {
+ CHECK(thread_checker_.CalledOnValidThread());
+ VLOG(1) << "ContinueTokenInitialization: " << tpm_token_state_;
+
+ switch (tpm_token_state_) {
+ case TPM_STATE_UNKNOWN: {
+ crypto_task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&CallOpenPersistentNSSDB),
+ base::Bind(&TPMTokenLoader::OnPersistentNSSDBOpened,
+ weak_factory_.GetWeakPtr()));
+ tpm_token_state_ = TPM_INITIALIZATION_STARTED;
+ return;
+ }
+ case TPM_INITIALIZATION_STARTED: {
+ NOTREACHED();
+ return;
+ }
+ case TPM_DB_OPENED: {
+ DBusThreadManager::Get()->GetCryptohomeClient()->TpmIsEnabled(
+ base::Bind(&TPMTokenLoader::OnTpmIsEnabled,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+ case TPM_DISABLED: {
+ // TPM is disabled, so proceed with empty tpm token name.
+ NotifyTPMTokenReady();
+ return;
+ }
+ case TPM_ENABLED: {
+ DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11IsTpmTokenReady(
+ base::Bind(&TPMTokenLoader::OnPkcs11IsTpmTokenReady,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+ case TPM_TOKEN_READY: {
+ // Retrieve token_name_ and user_pin_ here since they will never change
+ // and CryptohomeClient calls are not thread safe.
+ DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11GetTpmTokenInfo(
+ base::Bind(&TPMTokenLoader::OnPkcs11GetTpmTokenInfo,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+ case TPM_TOKEN_INFO_RECEIVED: {
+ base::PostTaskAndReplyWithResult(
+ crypto_task_runner_.get(),
+ FROM_HERE,
+ base::Bind(&crypto::InitializeTPMToken, tpm_token_slot_id_),
+ base::Bind(&TPMTokenLoader::OnTPMTokenInitialized,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+ case TPM_TOKEN_INITIALIZED: {
+ NotifyTPMTokenReady();
+ return;
+ }
+ }
+}
+
+void TPMTokenLoader::RetryTokenInitializationLater() {
+ CHECK(thread_checker_.CalledOnValidThread());
+ LOG(WARNING) << "Retry token initialization later.";
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&TPMTokenLoader::ContinueTokenInitialization,
+ weak_factory_.GetWeakPtr()),
+ tpm_request_delay_);
+ tpm_request_delay_ = GetNextRequestDelayMs(tpm_request_delay_);
+}
+
+void TPMTokenLoader::OnPersistentNSSDBOpened() {
+ VLOG(1) << "PersistentNSSDBOpened";
+ tpm_token_state_ = TPM_DB_OPENED;
+ ContinueTokenInitialization();
+}
+
+void TPMTokenLoader::OnTpmIsEnabled(DBusMethodCallStatus call_status,
+ bool tpm_is_enabled) {
+ VLOG(1) << "OnTpmIsEnabled: " << tpm_is_enabled;
+
+ if (call_status == DBUS_METHOD_CALL_SUCCESS && tpm_is_enabled)
+ tpm_token_state_ = TPM_ENABLED;
+ else
+ tpm_token_state_ = TPM_DISABLED;
+
+ ContinueTokenInitialization();
+}
+
+void TPMTokenLoader::OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status,
+ bool is_tpm_token_ready) {
+ VLOG(1) << "OnPkcs11IsTpmTokenReady: " << is_tpm_token_ready;
+
+ if (call_status == DBUS_METHOD_CALL_FAILURE || !is_tpm_token_ready) {
+ RetryTokenInitializationLater();
+ return;
+ }
+
+ tpm_token_state_ = TPM_TOKEN_READY;
+ ContinueTokenInitialization();
+}
+
+void TPMTokenLoader::OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status,
+ const std::string& token_name,
+ const std::string& user_pin,
+ int token_slot_id) {
+ VLOG(1) << "OnPkcs11GetTpmTokenInfo: " << token_name;
+
+ if (call_status == DBUS_METHOD_CALL_FAILURE) {
+ RetryTokenInitializationLater();
+ return;
+ }
+
+ tpm_token_name_ = token_name;
+ tpm_token_slot_id_ = token_slot_id;
+ tpm_user_pin_ = user_pin;
+ tpm_token_state_ = TPM_TOKEN_INFO_RECEIVED;
+
+ ContinueTokenInitialization();
+}
+
+void TPMTokenLoader::OnTPMTokenInitialized(bool success) {
+ VLOG(1) << "OnTPMTokenInitialized: " << success;
+ if (!success) {
+ RetryTokenInitializationLater();
+ return;
+ }
+ tpm_token_state_ = TPM_TOKEN_INITIALIZED;
+ ContinueTokenInitialization();
+}
+
+void TPMTokenLoader::NotifyTPMTokenReady() {
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnTPMTokenReady(tpm_user_pin_, tpm_token_name_, tpm_token_slot_id_));
+}
+
+void TPMTokenLoader::LoggedInStateChanged() {
+ VLOG(1) << "LoggedInStateChanged";
+ MaybeStartTokenInitialization();
+}
+
+} // namespace chromeos
diff --git a/chromeos/tpm_token_loader.h b/chromeos/tpm_token_loader.h
new file mode 100644
index 0000000..4d79833
--- /dev/null
+++ b/chromeos/tpm_token_loader.h
@@ -0,0 +1,147 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_TPM_TOKEN_LOADER_H_
+#define CHROMEOS_TPM_TOKEN_LOADER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_method_call_status.h"
+#include "chromeos/login/login_state.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace chromeos {
+
+// This class is responsible for loading the TPM token when the user logs
+// in. It is expected to be constructed on the UI thread and public methods
+// should all be called from the UI thread. When the TPM token is loaded,
+// or if the TPM should stay disabled for the session, the observers are
+// notified using |OnTPMTokenReady|.
+class CHROMEOS_EXPORT TPMTokenLoader : public LoginState::Observer {
+ public:
+ class Observer {
+ public:
+ virtual ~Observer() {}
+
+ // Called when the TPM token initialization is done or the case where TPM
+ // should stay disabled is detected (e.g. on guest login). If TPM is
+ // disabled, |tpm_user_pin|, |tpm_token_name| and |tpm_token_slot_id| will
+ // not be set.
+ virtual void OnTPMTokenReady(const std::string& tpm_user_pin,
+ const std::string& tpm_token_name,
+ int tpm_token_slot_id) = 0;
+ };
+
+ // Sets the global instance. Must be called before any calls to Get().
+ // The global instance will immediately start observing |LoginState|.
+ static void Initialize();
+
+ // Destroys the global instance.
+ static void Shutdown();
+
+ // Gets the global instance. Initialize() must be called before this.
+ static TPMTokenLoader* Get();
+
+ // Returns true if the global instance has been initialized.
+ static bool IsInitialized();
+
+ // By default, TPMTokenLoader tries to load the TPMToken only if running
+ // in a ChromeOS environment. Tests can call this function after Initialize()
+ // and before SetCryptoTaskRunner() to enable the TPM initialization.
+ void InitializeTPMForTest();
+
+ // |crypto_task_runner| is the task runner that any synchronous crypto calls
+ // should be made from, e.g. in Chrome this is the IO thread. Must be called
+ // after the thread is started. When called, this will attempt to start TPM
+ // token loading.
+ void SetCryptoTaskRunner(
+ const scoped_refptr<base::SequencedTaskRunner>& crypto_task_runner);
+
+ void AddObserver(TPMTokenLoader::Observer* observer);
+ void RemoveObserver(TPMTokenLoader::Observer* observer);
+
+ // Checks if the TPM token in ready to be used.
+ bool IsTPMTokenReady() const;
+
+ private:
+ TPMTokenLoader();
+ virtual ~TPMTokenLoader();
+
+ // Starts tpm token initialization if the user is logged in and the crypto
+ // task runner is set.
+ void MaybeStartTokenInitialization();
+
+ // This is the cyclic chain of callbacks to initialize the TPM token.
+ void ContinueTokenInitialization();
+ void OnPersistentNSSDBOpened();
+ void OnTpmIsEnabled(DBusMethodCallStatus call_status,
+ bool tpm_is_enabled);
+ void OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status,
+ bool is_tpm_token_ready);
+ void OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status,
+ const std::string& token_name,
+ const std::string& user_pin,
+ int token_slot_id);
+ void OnTPMTokenInitialized(bool success);
+
+ // If token initialization step fails (e.g. if tpm token is not yet ready)
+ // schedules the initialization step retry attempt after a timeout.
+ void RetryTokenInitializationLater();
+
+ // Notifies observers that the TPM token is ready.
+ void NotifyTPMTokenReady();
+
+ // LoginState::Observer
+ virtual void LoggedInStateChanged() OVERRIDE;
+
+ bool initialize_tpm_for_test_;
+
+ ObserverList<Observer> observers_;
+
+ // The states are traversed in this order but some might get omitted or never
+ // be left.
+ enum TPMTokenState {
+ TPM_STATE_UNKNOWN,
+ TPM_INITIALIZATION_STARTED,
+ TPM_DB_OPENED,
+ TPM_DISABLED,
+ TPM_ENABLED,
+ TPM_TOKEN_READY,
+ TPM_TOKEN_INFO_RECEIVED,
+ TPM_TOKEN_INITIALIZED,
+ };
+ TPMTokenState tpm_token_state_;
+
+ // The current request delay before the next attempt to initialize the
+ // TPM. Will be adapted after each attempt.
+ base::TimeDelta tpm_request_delay_;
+
+ // Cached TPM token info.
+ std::string tpm_token_name_;
+ int tpm_token_slot_id_;
+ std::string tpm_user_pin_;
+
+ base::ThreadChecker thread_checker_;
+
+ // TaskRunner for crypto calls.
+ scoped_refptr<base::SequencedTaskRunner> crypto_task_runner_;
+
+ base::WeakPtrFactory<TPMTokenLoader> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TPMTokenLoader);
+};
+
+} // namespace chromeos
+
+#endif // CHROMEOS_TPM_TOKEN_LOADER_H_