1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/tpm_token_loader.h"
10 #include "base/location.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/sys_info.h"
14 #include "base/task_runner_util.h"
15 #include "chromeos/dbus/cryptohome_client.h"
16 #include "chromeos/dbus/dbus_thread_manager.h"
17 #include "crypto/nss_util.h"
23 const int64 kInitialRequestDelayMs = 100;
24 const int64 kMaxRequestDelayMs = 300000; // 5 minutes
26 // Calculates the delay before running next attempt to initiatialize the TPM
27 // token, if |last_delay| was the last or initial delay.
28 base::TimeDelta GetNextRequestDelayMs(base::TimeDelta last_delay) {
29 // This implements an exponential backoff, as we don't know in which order of
30 // magnitude the TPM token changes it's state.
31 base::TimeDelta next_delay = last_delay * 2;
33 // Cap the delay to prevent an overflow. This threshold is arbitrarily chosen.
34 const base::TimeDelta max_delay =
35 base::TimeDelta::FromMilliseconds(kMaxRequestDelayMs);
36 if (next_delay > max_delay)
37 next_delay = max_delay;
41 void CallOpenPersistentNSSDB() {
42 // Called from crypto_task_runner_.
43 VLOG(1) << "CallOpenPersistentNSSDB";
45 // Ensure we've opened the user's key/certificate database.
46 if (base::SysInfo::IsRunningOnChromeOS())
47 crypto::OpenPersistentNSSDB();
48 crypto::EnableTPMTokenForNSS();
53 static TPMTokenLoader* g_tpm_token_loader = NULL;
56 void TPMTokenLoader::Initialize() {
57 CHECK(!g_tpm_token_loader);
58 g_tpm_token_loader = new TPMTokenLoader();
62 void TPMTokenLoader::Shutdown() {
63 CHECK(g_tpm_token_loader);
64 delete g_tpm_token_loader;
65 g_tpm_token_loader = NULL;
69 TPMTokenLoader* TPMTokenLoader::Get() {
70 CHECK(g_tpm_token_loader)
71 << "TPMTokenLoader::Get() called before Initialize()";
72 return g_tpm_token_loader;
76 bool TPMTokenLoader::IsInitialized() {
77 return g_tpm_token_loader;
80 TPMTokenLoader::TPMTokenLoader()
81 : initialize_tpm_for_test_(false),
82 tpm_token_state_(TPM_STATE_UNKNOWN),
84 base::TimeDelta::FromMilliseconds(kInitialRequestDelayMs)),
85 tpm_token_slot_id_(-1),
87 if (LoginState::IsInitialized())
88 LoginState::Get()->AddObserver(this);
91 void TPMTokenLoader::InitializeTPMForTest() {
92 initialize_tpm_for_test_ = true;
95 void TPMTokenLoader::SetCryptoTaskRunner(
96 const scoped_refptr<base::SequencedTaskRunner>& crypto_task_runner) {
97 crypto_task_runner_ = crypto_task_runner;
98 MaybeStartTokenInitialization();
101 TPMTokenLoader::~TPMTokenLoader() {
102 if (LoginState::IsInitialized())
103 LoginState::Get()->RemoveObserver(this);
106 void TPMTokenLoader::AddObserver(TPMTokenLoader::Observer* observer) {
107 observers_.AddObserver(observer);
110 void TPMTokenLoader::RemoveObserver(TPMTokenLoader::Observer* observer) {
111 observers_.RemoveObserver(observer);
114 bool TPMTokenLoader::IsTPMTokenReady() const {
115 return tpm_token_state_ == TPM_DISABLED ||
116 tpm_token_state_ == TPM_TOKEN_INITIALIZED;
119 void TPMTokenLoader::MaybeStartTokenInitialization() {
120 CHECK(thread_checker_.CalledOnValidThread());
122 // This is the entry point to the TPM token initialization process,
123 // which we should do at most once.
124 if (tpm_token_state_ != TPM_STATE_UNKNOWN || !crypto_task_runner_.get())
127 if (!LoginState::IsInitialized())
130 bool request_certificates = LoginState::Get()->IsUserLoggedIn() ||
131 LoginState::Get()->IsInSafeMode();
133 VLOG(1) << "RequestCertificates: " << request_certificates;
134 if (!request_certificates)
137 if (!initialize_tpm_for_test_ && !base::SysInfo::IsRunningOnChromeOS())
138 tpm_token_state_ = TPM_DISABLED;
140 // Treat TPM as disabled for guest users since they do not store certs.
141 if (LoginState::Get()->IsGuestUser())
142 tpm_token_state_ = TPM_DISABLED;
144 ContinueTokenInitialization();
146 DCHECK_NE(tpm_token_state_, TPM_STATE_UNKNOWN);
149 void TPMTokenLoader::ContinueTokenInitialization() {
150 CHECK(thread_checker_.CalledOnValidThread());
151 VLOG(1) << "ContinueTokenInitialization: " << tpm_token_state_;
153 switch (tpm_token_state_) {
154 case TPM_STATE_UNKNOWN: {
155 crypto_task_runner_->PostTaskAndReply(
157 base::Bind(&CallOpenPersistentNSSDB),
158 base::Bind(&TPMTokenLoader::OnPersistentNSSDBOpened,
159 weak_factory_.GetWeakPtr()));
160 tpm_token_state_ = TPM_INITIALIZATION_STARTED;
163 case TPM_INITIALIZATION_STARTED: {
167 case TPM_DB_OPENED: {
168 DBusThreadManager::Get()->GetCryptohomeClient()->TpmIsEnabled(
169 base::Bind(&TPMTokenLoader::OnTpmIsEnabled,
170 weak_factory_.GetWeakPtr()));
174 // TPM is disabled, so proceed with empty tpm token name.
175 NotifyTPMTokenReady();
179 DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11IsTpmTokenReady(
180 base::Bind(&TPMTokenLoader::OnPkcs11IsTpmTokenReady,
181 weak_factory_.GetWeakPtr()));
184 case TPM_TOKEN_READY: {
185 // Retrieve token_name_ and user_pin_ here since they will never change
186 // and CryptohomeClient calls are not thread safe.
187 DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11GetTpmTokenInfo(
188 base::Bind(&TPMTokenLoader::OnPkcs11GetTpmTokenInfo,
189 weak_factory_.GetWeakPtr()));
192 case TPM_TOKEN_INFO_RECEIVED: {
193 base::PostTaskAndReplyWithResult(
194 crypto_task_runner_.get(),
196 base::Bind(&crypto::InitializeTPMToken, tpm_token_slot_id_),
197 base::Bind(&TPMTokenLoader::OnTPMTokenInitialized,
198 weak_factory_.GetWeakPtr()));
201 case TPM_TOKEN_INITIALIZED: {
202 NotifyTPMTokenReady();
208 void TPMTokenLoader::RetryTokenInitializationLater() {
209 CHECK(thread_checker_.CalledOnValidThread());
210 LOG(WARNING) << "Retry token initialization later.";
211 base::MessageLoopProxy::current()->PostDelayedTask(
213 base::Bind(&TPMTokenLoader::ContinueTokenInitialization,
214 weak_factory_.GetWeakPtr()),
216 tpm_request_delay_ = GetNextRequestDelayMs(tpm_request_delay_);
219 void TPMTokenLoader::OnPersistentNSSDBOpened() {
220 VLOG(1) << "PersistentNSSDBOpened";
221 tpm_token_state_ = TPM_DB_OPENED;
222 ContinueTokenInitialization();
225 void TPMTokenLoader::OnTpmIsEnabled(DBusMethodCallStatus call_status,
226 bool tpm_is_enabled) {
227 VLOG(1) << "OnTpmIsEnabled: " << tpm_is_enabled;
229 if (call_status == DBUS_METHOD_CALL_SUCCESS && tpm_is_enabled)
230 tpm_token_state_ = TPM_ENABLED;
232 tpm_token_state_ = TPM_DISABLED;
234 ContinueTokenInitialization();
237 void TPMTokenLoader::OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status,
238 bool is_tpm_token_ready) {
239 VLOG(1) << "OnPkcs11IsTpmTokenReady: " << is_tpm_token_ready;
241 if (call_status == DBUS_METHOD_CALL_FAILURE || !is_tpm_token_ready) {
242 RetryTokenInitializationLater();
246 tpm_token_state_ = TPM_TOKEN_READY;
247 ContinueTokenInitialization();
250 void TPMTokenLoader::OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status,
251 const std::string& token_name,
252 const std::string& user_pin,
254 VLOG(1) << "OnPkcs11GetTpmTokenInfo: " << token_name;
256 if (call_status == DBUS_METHOD_CALL_FAILURE) {
257 RetryTokenInitializationLater();
261 tpm_token_name_ = token_name;
262 tpm_token_slot_id_ = token_slot_id;
263 tpm_user_pin_ = user_pin;
264 tpm_token_state_ = TPM_TOKEN_INFO_RECEIVED;
266 ContinueTokenInitialization();
269 void TPMTokenLoader::OnTPMTokenInitialized(bool success) {
270 VLOG(1) << "OnTPMTokenInitialized: " << success;
272 RetryTokenInitializationLater();
275 tpm_token_state_ = TPM_TOKEN_INITIALIZED;
276 ContinueTokenInitialization();
279 void TPMTokenLoader::NotifyTPMTokenReady() {
280 FOR_EACH_OBSERVER(Observer, observers_,
281 OnTPMTokenReady(tpm_user_pin_, tpm_token_name_, tpm_token_slot_id_));
284 void TPMTokenLoader::LoggedInStateChanged() {
285 VLOG(1) << "LoggedInStateChanged";
286 MaybeStartTokenInitialization();
289 } // namespace chromeos