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 PostResultToTaskRunner(scoped_refptr
<base::SequencedTaskRunner
> runner
,
42 const base::Callback
<void(bool)>& callback
,
44 runner
->PostTask(FROM_HERE
, base::Bind(callback
, success
));
49 static TPMTokenLoader
* g_tpm_token_loader
= NULL
;
52 void TPMTokenLoader::Initialize() {
53 CHECK(!g_tpm_token_loader
);
54 g_tpm_token_loader
= new TPMTokenLoader(false /*for_test*/);
58 void TPMTokenLoader::InitializeForTest() {
59 CHECK(!g_tpm_token_loader
);
60 g_tpm_token_loader
= new TPMTokenLoader(true /*for_test*/);
64 void TPMTokenLoader::Shutdown() {
65 CHECK(g_tpm_token_loader
);
66 delete g_tpm_token_loader
;
67 g_tpm_token_loader
= NULL
;
71 TPMTokenLoader
* TPMTokenLoader::Get() {
72 CHECK(g_tpm_token_loader
)
73 << "TPMTokenLoader::Get() called before Initialize()";
74 return g_tpm_token_loader
;
78 bool TPMTokenLoader::IsInitialized() {
79 return g_tpm_token_loader
;
82 TPMTokenLoader::TPMTokenLoader(bool for_test
)
83 : initialized_for_test_(for_test
),
84 tpm_token_state_(TPM_STATE_UNKNOWN
),
86 base::TimeDelta::FromMilliseconds(kInitialRequestDelayMs
)),
87 tpm_token_slot_id_(-1),
89 if (!initialized_for_test_
&& LoginState::IsInitialized())
90 LoginState::Get()->AddObserver(this);
92 if (initialized_for_test_
) {
93 tpm_token_state_
= TPM_TOKEN_INITIALIZED
;
94 tpm_user_pin_
= "111111";
98 void TPMTokenLoader::SetCryptoTaskRunner(
99 const scoped_refptr
<base::SequencedTaskRunner
>& crypto_task_runner
) {
100 crypto_task_runner_
= crypto_task_runner
;
101 MaybeStartTokenInitialization();
104 TPMTokenLoader::~TPMTokenLoader() {
105 if (!initialized_for_test_
&& LoginState::IsInitialized())
106 LoginState::Get()->RemoveObserver(this);
109 void TPMTokenLoader::AddObserver(TPMTokenLoader::Observer
* observer
) {
110 observers_
.AddObserver(observer
);
113 void TPMTokenLoader::RemoveObserver(TPMTokenLoader::Observer
* observer
) {
114 observers_
.RemoveObserver(observer
);
117 bool TPMTokenLoader::IsTPMTokenReady() const {
118 return tpm_token_state_
== TPM_DISABLED
||
119 tpm_token_state_
== TPM_TOKEN_INITIALIZED
;
122 void TPMTokenLoader::MaybeStartTokenInitialization() {
123 CHECK(thread_checker_
.CalledOnValidThread());
125 // This is the entry point to the TPM token initialization process,
126 // which we should do at most once.
127 if (tpm_token_state_
!= TPM_STATE_UNKNOWN
|| !crypto_task_runner_
.get())
130 if (!LoginState::IsInitialized())
133 bool start_initialization
= LoginState::Get()->IsUserLoggedIn();
135 VLOG(1) << "StartTokenInitialization: " << start_initialization
;
136 if (!start_initialization
)
139 if (!base::SysInfo::IsRunningOnChromeOS())
140 tpm_token_state_
= TPM_DISABLED
;
142 // Treat TPM as disabled for guest users since they do not store certs.
143 if (LoginState::Get()->IsGuestUser())
144 tpm_token_state_
= TPM_DISABLED
;
146 ContinueTokenInitialization();
148 DCHECK_NE(tpm_token_state_
, TPM_STATE_UNKNOWN
);
151 void TPMTokenLoader::ContinueTokenInitialization() {
152 CHECK(thread_checker_
.CalledOnValidThread());
153 VLOG(1) << "ContinueTokenInitialization: " << tpm_token_state_
;
155 switch (tpm_token_state_
) {
156 case TPM_STATE_UNKNOWN
: {
157 crypto_task_runner_
->PostTaskAndReply(
159 base::Bind(&crypto::EnableTPMTokenForNSS
),
160 base::Bind(&TPMTokenLoader::OnTPMTokenEnabledForNSS
,
161 weak_factory_
.GetWeakPtr()));
162 tpm_token_state_
= TPM_INITIALIZATION_STARTED
;
165 case TPM_INITIALIZATION_STARTED
: {
169 case TPM_TOKEN_ENABLED_FOR_NSS
: {
170 DBusThreadManager::Get()->GetCryptohomeClient()->TpmIsEnabled(
171 base::Bind(&TPMTokenLoader::OnTpmIsEnabled
,
172 weak_factory_
.GetWeakPtr()));
176 // TPM is disabled, so proceed with empty tpm token name.
177 NotifyTPMTokenReady();
181 DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11IsTpmTokenReady(
182 base::Bind(&TPMTokenLoader::OnPkcs11IsTpmTokenReady
,
183 weak_factory_
.GetWeakPtr()));
186 case TPM_TOKEN_READY
: {
187 // Retrieve token_name_ and user_pin_ here since they will never change
188 // and CryptohomeClient calls are not thread safe.
189 DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11GetTpmTokenInfo(
190 base::Bind(&TPMTokenLoader::OnPkcs11GetTpmTokenInfo
,
191 weak_factory_
.GetWeakPtr()));
194 case TPM_TOKEN_INFO_RECEIVED
: {
195 crypto_task_runner_
->PostTask(
198 &crypto::InitializeTPMTokenAndSystemSlot
,
200 base::Bind(&PostResultToTaskRunner
,
201 base::MessageLoopProxy::current(),
202 base::Bind(&TPMTokenLoader::OnTPMTokenInitialized
,
203 weak_factory_
.GetWeakPtr()))));
206 case TPM_TOKEN_INITIALIZED
: {
207 NotifyTPMTokenReady();
213 void TPMTokenLoader::RetryTokenInitializationLater() {
214 CHECK(thread_checker_
.CalledOnValidThread());
215 VLOG(1) << "Retry token initialization later.";
216 base::MessageLoopProxy::current()->PostDelayedTask(
218 base::Bind(&TPMTokenLoader::ContinueTokenInitialization
,
219 weak_factory_
.GetWeakPtr()),
221 tpm_request_delay_
= GetNextRequestDelayMs(tpm_request_delay_
);
224 void TPMTokenLoader::OnTPMTokenEnabledForNSS() {
225 VLOG(1) << "TPMTokenEnabledForNSS";
226 tpm_token_state_
= TPM_TOKEN_ENABLED_FOR_NSS
;
227 ContinueTokenInitialization();
230 void TPMTokenLoader::OnTpmIsEnabled(DBusMethodCallStatus call_status
,
231 bool tpm_is_enabled
) {
232 VLOG(1) << "OnTpmIsEnabled: " << tpm_is_enabled
;
234 if (call_status
== DBUS_METHOD_CALL_SUCCESS
&& tpm_is_enabled
)
235 tpm_token_state_
= TPM_ENABLED
;
237 tpm_token_state_
= TPM_DISABLED
;
239 ContinueTokenInitialization();
242 void TPMTokenLoader::OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status
,
243 bool is_tpm_token_ready
) {
244 VLOG(1) << "OnPkcs11IsTpmTokenReady: " << is_tpm_token_ready
;
246 if (call_status
== DBUS_METHOD_CALL_FAILURE
|| !is_tpm_token_ready
) {
247 RetryTokenInitializationLater();
251 tpm_token_state_
= TPM_TOKEN_READY
;
252 ContinueTokenInitialization();
255 void TPMTokenLoader::OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status
,
256 const std::string
& token_name
,
257 const std::string
& user_pin
,
259 VLOG(1) << "OnPkcs11GetTpmTokenInfo: " << token_name
;
261 if (call_status
== DBUS_METHOD_CALL_FAILURE
) {
262 RetryTokenInitializationLater();
266 tpm_token_name_
= token_name
;
267 tpm_token_slot_id_
= token_slot_id
;
268 tpm_user_pin_
= user_pin
;
269 tpm_token_state_
= TPM_TOKEN_INFO_RECEIVED
;
271 ContinueTokenInitialization();
274 void TPMTokenLoader::OnTPMTokenInitialized(bool success
) {
275 VLOG(1) << "OnTPMTokenInitialized: " << success
;
277 RetryTokenInitializationLater();
280 tpm_token_state_
= TPM_TOKEN_INITIALIZED
;
281 ContinueTokenInitialization();
284 void TPMTokenLoader::NotifyTPMTokenReady() {
285 FOR_EACH_OBSERVER(Observer
, observers_
, OnTPMTokenReady());
288 void TPMTokenLoader::LoggedInStateChanged() {
289 VLOG(1) << "LoggedInStateChanged";
290 MaybeStartTokenInitialization();
293 } // namespace chromeos