diff options
author | tbarzic@chromium.org <tbarzic@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-21 23:14:53 +0000 |
---|---|---|
committer | tbarzic@chromium.org <tbarzic@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-21 23:14:53 +0000 |
commit | b2390837986574afa95926297767fad126bc9b74 (patch) | |
tree | 8744896918af941c5850db968d7f71bf428dfea4 | |
parent | b2d1f2dcf6d9181cd8b60804e6cbf0c13fbf9a93 (diff) | |
download | chromium_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
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_ |