// 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/tpm_token_loader.h" #include #include "base/bind.h" #include "base/location.h" #include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" #include "base/sys_info.h" #include "base/task_runner_util.h" #include "base/thread_task_runner_handle.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/tpm/tpm_token_info_getter.h" #include "crypto/nss_util.h" namespace chromeos { namespace { void PostResultToTaskRunner(scoped_refptr runner, const base::Callback& callback, bool success) { runner->PostTask(FROM_HERE, base::Bind(callback, success)); } } // namespace static TPMTokenLoader* g_tpm_token_loader = NULL; // static void TPMTokenLoader::Initialize() { CHECK(!g_tpm_token_loader); g_tpm_token_loader = new TPMTokenLoader(false /*for_test*/); } // static void TPMTokenLoader::InitializeForTest() { CHECK(!g_tpm_token_loader); g_tpm_token_loader = new TPMTokenLoader(true /*for_test*/); } // 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(bool for_test) : initialized_for_test_(for_test), tpm_token_state_(TPM_STATE_UNKNOWN), tpm_token_info_getter_( TPMTokenInfoGetter::CreateForSystemToken( DBusThreadManager::Get()->GetCryptohomeClient(), base::ThreadTaskRunnerHandle::Get())), tpm_token_slot_id_(-1), can_start_before_login_(false), weak_factory_(this) { if (!initialized_for_test_ && LoginState::IsInitialized()) LoginState::Get()->AddObserver(this); if (initialized_for_test_) { tpm_token_state_ = TPM_TOKEN_INITIALIZED; tpm_user_pin_ = "111111"; } } void TPMTokenLoader::SetCryptoTaskRunner( const scoped_refptr& crypto_task_runner) { crypto_task_runner_ = crypto_task_runner; MaybeStartTokenInitialization(); } void TPMTokenLoader::EnsureStarted() { if (can_start_before_login_) return; can_start_before_login_ = true; MaybeStartTokenInitialization(); } TPMTokenLoader::~TPMTokenLoader() { if (!initialized_for_test_ && LoginState::IsInitialized()) LoginState::Get()->RemoveObserver(this); } TPMTokenLoader::TPMTokenStatus TPMTokenLoader::IsTPMTokenEnabled( const TPMReadyCallback& callback) { if (tpm_token_state_ == TPM_TOKEN_INITIALIZED) return TPM_TOKEN_STATUS_ENABLED; if (!IsTPMLoadingEnabled() || tpm_token_state_ == TPM_DISABLED) return TPM_TOKEN_STATUS_DISABLED; // Status is not known yet. if (!callback.is_null()) tpm_ready_callback_list_.push_back(callback); return TPM_TOKEN_STATUS_UNDETERMINED; } bool TPMTokenLoader::IsTPMLoadingEnabled() const { // TPM loading is enabled on non-ChromeOS environments, e.g. when running // tests on Linux. // Treat TPM as disabled for guest users since they do not store certs. return initialized_for_test_ || (base::SysInfo::IsRunningOnChromeOS() && !LoginState::Get()->IsGuestSessionUser()); } 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; bool start_initialization = (LoginState::IsInitialized() && LoginState::Get()->IsUserLoggedIn()) || can_start_before_login_; VLOG(1) << "StartTokenInitialization: " << start_initialization; if (!start_initialization) return; if (!IsTPMLoadingEnabled()) 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(&crypto::EnableTPMTokenForNSS), base::Bind(&TPMTokenLoader::OnTPMTokenEnabledForNSS, weak_factory_.GetWeakPtr())); tpm_token_state_ = TPM_INITIALIZATION_STARTED; return; } case TPM_INITIALIZATION_STARTED: { NOTREACHED(); return; } case TPM_TOKEN_ENABLED_FOR_NSS: { tpm_token_info_getter_->Start( base::Bind(&TPMTokenLoader::OnGotTpmTokenInfo, weak_factory_.GetWeakPtr())); return; } case TPM_DISABLED: { // TPM is disabled, so proceed with empty tpm token name. NotifyTPMTokenReady(); return; } case TPM_TOKEN_INFO_RECEIVED: { crypto_task_runner_->PostTask( FROM_HERE, base::Bind( &crypto::InitializeTPMTokenAndSystemSlot, tpm_token_slot_id_, base::Bind(&PostResultToTaskRunner, base::ThreadTaskRunnerHandle::Get(), base::Bind(&TPMTokenLoader::OnTPMTokenInitialized, weak_factory_.GetWeakPtr())))); return; } case TPM_TOKEN_INITIALIZED: { NotifyTPMTokenReady(); return; } } } void TPMTokenLoader::OnTPMTokenEnabledForNSS() { VLOG(1) << "TPMTokenEnabledForNSS"; tpm_token_state_ = TPM_TOKEN_ENABLED_FOR_NSS; ContinueTokenInitialization(); } void TPMTokenLoader::OnGotTpmTokenInfo(const TPMTokenInfo& token_info) { if (!token_info.tpm_is_enabled) { tpm_token_state_ = TPM_DISABLED; ContinueTokenInitialization(); return; } tpm_token_slot_id_ = token_info.token_slot_id; tpm_user_pin_ = token_info.user_pin; tpm_token_state_ = TPM_TOKEN_INFO_RECEIVED; ContinueTokenInitialization(); } void TPMTokenLoader::OnTPMTokenInitialized(bool success) { VLOG(1) << "OnTPMTokenInitialized: " << success; tpm_token_state_ = success ? TPM_TOKEN_INITIALIZED : TPM_DISABLED; ContinueTokenInitialization(); } void TPMTokenLoader::NotifyTPMTokenReady() { DCHECK(tpm_token_state_ == TPM_DISABLED || tpm_token_state_ == TPM_TOKEN_INITIALIZED); bool tpm_status = tpm_token_state_ == TPM_TOKEN_INITIALIZED; for (TPMReadyCallbackList::iterator i = tpm_ready_callback_list_.begin(); i != tpm_ready_callback_list_.end(); ++i) { i->Run(tpm_status); } tpm_ready_callback_list_.clear(); } void TPMTokenLoader::LoggedInStateChanged() { VLOG(1) << "LoggedInStateChanged"; MaybeStartTokenInitialization(); } } // namespace chromeos