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 TPMTokenLoader::TPMTokenStatus
TPMTokenLoader::IsTPMTokenEnabled(
110 const TPMReadyCallback
& callback
) {
111 if (tpm_token_state_
== TPM_TOKEN_INITIALIZED
)
112 return TPM_TOKEN_STATUS_ENABLED
;
113 if (!IsTPMLoadingEnabled() || tpm_token_state_
== TPM_DISABLED
)
114 return TPM_TOKEN_STATUS_DISABLED
;
115 // Status is not known yet.
116 if (!callback
.is_null())
117 tpm_ready_callback_list_
.push_back(callback
);
118 return TPM_TOKEN_STATUS_UNDETERMINED
;
121 bool TPMTokenLoader::IsTPMLoadingEnabled() const {
122 // TPM loading is enabled on non-ChromeOS environments, e.g. when running
124 // Treat TPM as disabled for guest users since they do not store certs.
125 return initialized_for_test_
|| (base::SysInfo::IsRunningOnChromeOS() &&
126 !LoginState::Get()->IsGuestSessionUser());
129 void TPMTokenLoader::MaybeStartTokenInitialization() {
130 CHECK(thread_checker_
.CalledOnValidThread());
132 // This is the entry point to the TPM token initialization process,
133 // which we should do at most once.
134 if (tpm_token_state_
!= TPM_STATE_UNKNOWN
|| !crypto_task_runner_
.get())
137 if (!LoginState::IsInitialized())
140 bool start_initialization
= LoginState::Get()->IsUserLoggedIn();
142 VLOG(1) << "StartTokenInitialization: " << start_initialization
;
143 if (!start_initialization
)
146 if (!IsTPMLoadingEnabled())
147 tpm_token_state_
= TPM_DISABLED
;
149 ContinueTokenInitialization();
151 DCHECK_NE(tpm_token_state_
, TPM_STATE_UNKNOWN
);
154 void TPMTokenLoader::ContinueTokenInitialization() {
155 CHECK(thread_checker_
.CalledOnValidThread());
156 VLOG(1) << "ContinueTokenInitialization: " << tpm_token_state_
;
158 switch (tpm_token_state_
) {
159 case TPM_STATE_UNKNOWN
: {
160 crypto_task_runner_
->PostTaskAndReply(
162 base::Bind(&crypto::EnableTPMTokenForNSS
),
163 base::Bind(&TPMTokenLoader::OnTPMTokenEnabledForNSS
,
164 weak_factory_
.GetWeakPtr()));
165 tpm_token_state_
= TPM_INITIALIZATION_STARTED
;
168 case TPM_INITIALIZATION_STARTED
: {
172 case TPM_TOKEN_ENABLED_FOR_NSS
: {
173 DBusThreadManager::Get()->GetCryptohomeClient()->TpmIsEnabled(
174 base::Bind(&TPMTokenLoader::OnTpmIsEnabled
,
175 weak_factory_
.GetWeakPtr()));
179 // TPM is disabled, so proceed with empty tpm token name.
180 NotifyTPMTokenReady();
184 DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11IsTpmTokenReady(
185 base::Bind(&TPMTokenLoader::OnPkcs11IsTpmTokenReady
,
186 weak_factory_
.GetWeakPtr()));
189 case TPM_TOKEN_READY
: {
190 // Retrieve user_pin_ here since they will never change
191 // and CryptohomeClient calls are not thread safe.
192 DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11GetTpmTokenInfo(
193 base::Bind(&TPMTokenLoader::OnPkcs11GetTpmTokenInfo
,
194 weak_factory_
.GetWeakPtr()));
197 case TPM_TOKEN_INFO_RECEIVED
: {
198 crypto_task_runner_
->PostTask(
201 &crypto::InitializeTPMTokenAndSystemSlot
,
203 base::Bind(&PostResultToTaskRunner
,
204 base::MessageLoopProxy::current(),
205 base::Bind(&TPMTokenLoader::OnTPMTokenInitialized
,
206 weak_factory_
.GetWeakPtr()))));
209 case TPM_TOKEN_INITIALIZED
: {
210 NotifyTPMTokenReady();
216 void TPMTokenLoader::RetryTokenInitializationLater() {
217 CHECK(thread_checker_
.CalledOnValidThread());
218 VLOG(1) << "Retry token initialization later.";
219 base::MessageLoopProxy::current()->PostDelayedTask(
221 base::Bind(&TPMTokenLoader::ContinueTokenInitialization
,
222 weak_factory_
.GetWeakPtr()),
224 tpm_request_delay_
= GetNextRequestDelayMs(tpm_request_delay_
);
227 void TPMTokenLoader::OnTPMTokenEnabledForNSS() {
228 VLOG(1) << "TPMTokenEnabledForNSS";
229 tpm_token_state_
= TPM_TOKEN_ENABLED_FOR_NSS
;
230 ContinueTokenInitialization();
233 void TPMTokenLoader::OnTpmIsEnabled(DBusMethodCallStatus call_status
,
234 bool tpm_is_enabled
) {
235 VLOG(1) << "OnTpmIsEnabled: " << tpm_is_enabled
;
237 if (call_status
== DBUS_METHOD_CALL_SUCCESS
&& tpm_is_enabled
)
238 tpm_token_state_
= TPM_ENABLED
;
240 tpm_token_state_
= TPM_DISABLED
;
242 ContinueTokenInitialization();
245 void TPMTokenLoader::OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status
,
246 bool is_tpm_token_ready
) {
247 VLOG(1) << "OnPkcs11IsTpmTokenReady: " << is_tpm_token_ready
;
249 if (call_status
== DBUS_METHOD_CALL_FAILURE
|| !is_tpm_token_ready
) {
250 RetryTokenInitializationLater();
254 tpm_token_state_
= TPM_TOKEN_READY
;
255 ContinueTokenInitialization();
258 void TPMTokenLoader::OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status
,
259 const std::string
& token_name
,
260 const std::string
& user_pin
,
262 VLOG(1) << "OnPkcs11GetTpmTokenInfo: " << token_name
;
264 if (call_status
== DBUS_METHOD_CALL_FAILURE
) {
265 RetryTokenInitializationLater();
269 tpm_token_slot_id_
= token_slot_id
;
270 tpm_user_pin_
= user_pin
;
271 tpm_token_state_
= TPM_TOKEN_INFO_RECEIVED
;
273 ContinueTokenInitialization();
276 void TPMTokenLoader::OnTPMTokenInitialized(bool success
) {
277 VLOG(1) << "OnTPMTokenInitialized: " << success
;
279 RetryTokenInitializationLater();
282 tpm_token_state_
= TPM_TOKEN_INITIALIZED
;
283 ContinueTokenInitialization();
286 void TPMTokenLoader::NotifyTPMTokenReady() {
287 DCHECK(tpm_token_state_
== TPM_DISABLED
||
288 tpm_token_state_
== TPM_TOKEN_INITIALIZED
);
289 bool tpm_status
= tpm_token_state_
== TPM_TOKEN_INITIALIZED
;
290 for (TPMReadyCallbackList::iterator i
= tpm_ready_callback_list_
.begin();
291 i
!= tpm_ready_callback_list_
.end();
295 tpm_ready_callback_list_
.clear();
298 void TPMTokenLoader::LoggedInStateChanged() {
299 VLOG(1) << "LoggedInStateChanged";
300 MaybeStartTokenInitialization();
303 } // namespace chromeos