• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chromeos/tpm_token_loader.h"
6 
7 #include <algorithm>
8 
9 #include "base/bind.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"
18 
19 namespace chromeos {
20 
21 namespace {
22 
23 const int64 kInitialRequestDelayMs = 100;
24 const int64 kMaxRequestDelayMs = 300000;  // 5 minutes
25 
26 // Calculates the delay before running next attempt to initiatialize the TPM
27 // token, if |last_delay| was the last or initial delay.
GetNextRequestDelayMs(base::TimeDelta last_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;
32 
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;
38   return next_delay;
39 }
40 
CallOpenPersistentNSSDB()41 void CallOpenPersistentNSSDB() {
42   // Called from crypto_task_runner_.
43   VLOG(1) << "CallOpenPersistentNSSDB";
44 
45   // Ensure we've opened the user's key/certificate database.
46   if (base::SysInfo::IsRunningOnChromeOS())
47     crypto::OpenPersistentNSSDB();
48   crypto::EnableTPMTokenForNSS();
49 }
50 
PostResultToTaskRunner(scoped_refptr<base::SequencedTaskRunner> runner,const base::Callback<void (bool)> & callback,bool success)51 void PostResultToTaskRunner(scoped_refptr<base::SequencedTaskRunner> runner,
52                             const base::Callback<void(bool)>& callback,
53                             bool success) {
54   runner->PostTask(FROM_HERE, base::Bind(callback, success));
55 }
56 
57 }  // namespace
58 
59 static TPMTokenLoader* g_tpm_token_loader = NULL;
60 
61 // static
Initialize()62 void TPMTokenLoader::Initialize() {
63   CHECK(!g_tpm_token_loader);
64   g_tpm_token_loader = new TPMTokenLoader(false /*for_test*/);
65 }
66 
67 // static
InitializeForTest()68 void TPMTokenLoader::InitializeForTest() {
69   CHECK(!g_tpm_token_loader);
70   g_tpm_token_loader = new TPMTokenLoader(true /*for_test*/);
71 }
72 
73 // static
Shutdown()74 void TPMTokenLoader::Shutdown() {
75   CHECK(g_tpm_token_loader);
76   delete g_tpm_token_loader;
77   g_tpm_token_loader = NULL;
78 }
79 
80 // static
Get()81 TPMTokenLoader* TPMTokenLoader::Get() {
82   CHECK(g_tpm_token_loader)
83       << "TPMTokenLoader::Get() called before Initialize()";
84   return g_tpm_token_loader;
85 }
86 
87 // static
IsInitialized()88 bool TPMTokenLoader::IsInitialized() {
89   return g_tpm_token_loader;
90 }
91 
TPMTokenLoader(bool for_test)92 TPMTokenLoader::TPMTokenLoader(bool for_test)
93     : initialized_for_test_(for_test),
94       tpm_token_state_(TPM_STATE_UNKNOWN),
95       tpm_request_delay_(
96           base::TimeDelta::FromMilliseconds(kInitialRequestDelayMs)),
97       tpm_token_slot_id_(-1),
98       weak_factory_(this) {
99   if (!initialized_for_test_ && LoginState::IsInitialized())
100     LoginState::Get()->AddObserver(this);
101 
102   if (initialized_for_test_) {
103     tpm_token_state_ = TPM_TOKEN_INITIALIZED;
104     tpm_user_pin_ = "111111";
105   }
106 }
107 
SetCryptoTaskRunner(const scoped_refptr<base::SequencedTaskRunner> & crypto_task_runner)108 void TPMTokenLoader::SetCryptoTaskRunner(
109     const scoped_refptr<base::SequencedTaskRunner>& crypto_task_runner) {
110   crypto_task_runner_ = crypto_task_runner;
111   MaybeStartTokenInitialization();
112 }
113 
~TPMTokenLoader()114 TPMTokenLoader::~TPMTokenLoader() {
115   if (!initialized_for_test_ && LoginState::IsInitialized())
116     LoginState::Get()->RemoveObserver(this);
117 }
118 
AddObserver(TPMTokenLoader::Observer * observer)119 void TPMTokenLoader::AddObserver(TPMTokenLoader::Observer* observer) {
120   observers_.AddObserver(observer);
121 }
122 
RemoveObserver(TPMTokenLoader::Observer * observer)123 void TPMTokenLoader::RemoveObserver(TPMTokenLoader::Observer* observer) {
124   observers_.RemoveObserver(observer);
125 }
126 
IsTPMTokenReady() const127 bool TPMTokenLoader::IsTPMTokenReady() const {
128   return tpm_token_state_ == TPM_DISABLED ||
129          tpm_token_state_ == TPM_TOKEN_INITIALIZED;
130 }
131 
MaybeStartTokenInitialization()132 void TPMTokenLoader::MaybeStartTokenInitialization() {
133   CHECK(thread_checker_.CalledOnValidThread());
134 
135   // This is the entry point to the TPM token initialization process,
136   // which we should do at most once.
137   if (tpm_token_state_ != TPM_STATE_UNKNOWN || !crypto_task_runner_.get())
138     return;
139 
140   if (!LoginState::IsInitialized())
141     return;
142 
143   bool start_initialization = LoginState::Get()->IsUserLoggedIn() ||
144       LoginState::Get()->IsInSafeMode();
145 
146   VLOG(1) << "StartTokenInitialization: " << start_initialization;
147   if (!start_initialization)
148     return;
149 
150   if (!base::SysInfo::IsRunningOnChromeOS())
151     tpm_token_state_ = TPM_DISABLED;
152 
153   // Treat TPM as disabled for guest users since they do not store certs.
154   if (LoginState::Get()->IsGuestUser())
155     tpm_token_state_ = TPM_DISABLED;
156 
157   ContinueTokenInitialization();
158 
159   DCHECK_NE(tpm_token_state_, TPM_STATE_UNKNOWN);
160 }
161 
ContinueTokenInitialization()162 void TPMTokenLoader::ContinueTokenInitialization() {
163   CHECK(thread_checker_.CalledOnValidThread());
164   VLOG(1) << "ContinueTokenInitialization: " << tpm_token_state_;
165 
166   switch (tpm_token_state_) {
167     case TPM_STATE_UNKNOWN: {
168       crypto_task_runner_->PostTaskAndReply(
169           FROM_HERE,
170           base::Bind(&CallOpenPersistentNSSDB),
171           base::Bind(&TPMTokenLoader::OnPersistentNSSDBOpened,
172                      weak_factory_.GetWeakPtr()));
173       tpm_token_state_ = TPM_INITIALIZATION_STARTED;
174       return;
175     }
176     case TPM_INITIALIZATION_STARTED: {
177       NOTREACHED();
178       return;
179     }
180     case TPM_DB_OPENED: {
181       DBusThreadManager::Get()->GetCryptohomeClient()->TpmIsEnabled(
182           base::Bind(&TPMTokenLoader::OnTpmIsEnabled,
183                      weak_factory_.GetWeakPtr()));
184       return;
185     }
186     case TPM_DISABLED: {
187       // TPM is disabled, so proceed with empty tpm token name.
188       NotifyTPMTokenReady();
189       return;
190     }
191     case TPM_ENABLED: {
192       DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11IsTpmTokenReady(
193           base::Bind(&TPMTokenLoader::OnPkcs11IsTpmTokenReady,
194                      weak_factory_.GetWeakPtr()));
195       return;
196     }
197     case TPM_TOKEN_READY: {
198       // Retrieve token_name_ and user_pin_ here since they will never change
199       // and CryptohomeClient calls are not thread safe.
200       DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11GetTpmTokenInfo(
201           base::Bind(&TPMTokenLoader::OnPkcs11GetTpmTokenInfo,
202                      weak_factory_.GetWeakPtr()));
203       return;
204     }
205     case TPM_TOKEN_INFO_RECEIVED: {
206       crypto_task_runner_->PostTask(
207           FROM_HERE,
208           base::Bind(
209               &crypto::InitializeTPMToken,
210               tpm_token_slot_id_,
211               base::Bind(&PostResultToTaskRunner,
212                          base::MessageLoopProxy::current(),
213                          base::Bind(&TPMTokenLoader::OnTPMTokenInitialized,
214                                     weak_factory_.GetWeakPtr()))));
215       return;
216     }
217     case TPM_TOKEN_INITIALIZED: {
218       NotifyTPMTokenReady();
219       return;
220     }
221   }
222 }
223 
RetryTokenInitializationLater()224 void TPMTokenLoader::RetryTokenInitializationLater() {
225   CHECK(thread_checker_.CalledOnValidThread());
226   LOG(WARNING) << "Retry token initialization later.";
227   base::MessageLoopProxy::current()->PostDelayedTask(
228       FROM_HERE,
229       base::Bind(&TPMTokenLoader::ContinueTokenInitialization,
230                  weak_factory_.GetWeakPtr()),
231       tpm_request_delay_);
232   tpm_request_delay_ = GetNextRequestDelayMs(tpm_request_delay_);
233 }
234 
OnPersistentNSSDBOpened()235 void TPMTokenLoader::OnPersistentNSSDBOpened() {
236   VLOG(1) << "PersistentNSSDBOpened";
237   tpm_token_state_ = TPM_DB_OPENED;
238   ContinueTokenInitialization();
239 }
240 
OnTpmIsEnabled(DBusMethodCallStatus call_status,bool tpm_is_enabled)241 void TPMTokenLoader::OnTpmIsEnabled(DBusMethodCallStatus call_status,
242                                     bool tpm_is_enabled) {
243   VLOG(1) << "OnTpmIsEnabled: " << tpm_is_enabled;
244 
245   if (call_status == DBUS_METHOD_CALL_SUCCESS && tpm_is_enabled)
246     tpm_token_state_ = TPM_ENABLED;
247   else
248     tpm_token_state_ = TPM_DISABLED;
249 
250   ContinueTokenInitialization();
251 }
252 
OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status,bool is_tpm_token_ready)253 void TPMTokenLoader::OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status,
254                                          bool is_tpm_token_ready) {
255   VLOG(1) << "OnPkcs11IsTpmTokenReady: " << is_tpm_token_ready;
256 
257   if (call_status == DBUS_METHOD_CALL_FAILURE || !is_tpm_token_ready) {
258     RetryTokenInitializationLater();
259     return;
260   }
261 
262   tpm_token_state_ = TPM_TOKEN_READY;
263   ContinueTokenInitialization();
264 }
265 
OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status,const std::string & token_name,const std::string & user_pin,int token_slot_id)266 void TPMTokenLoader::OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status,
267                                              const std::string& token_name,
268                                              const std::string& user_pin,
269                                              int token_slot_id) {
270   VLOG(1) << "OnPkcs11GetTpmTokenInfo: " << token_name;
271 
272   if (call_status == DBUS_METHOD_CALL_FAILURE) {
273     RetryTokenInitializationLater();
274     return;
275   }
276 
277   tpm_token_name_ = token_name;
278   tpm_token_slot_id_ = token_slot_id;
279   tpm_user_pin_ = user_pin;
280   tpm_token_state_ = TPM_TOKEN_INFO_RECEIVED;
281 
282   ContinueTokenInitialization();
283 }
284 
OnTPMTokenInitialized(bool success)285 void TPMTokenLoader::OnTPMTokenInitialized(bool success) {
286   VLOG(1) << "OnTPMTokenInitialized: " << success;
287   if (!success) {
288     RetryTokenInitializationLater();
289     return;
290   }
291   tpm_token_state_ = TPM_TOKEN_INITIALIZED;
292   ContinueTokenInitialization();
293 }
294 
NotifyTPMTokenReady()295 void TPMTokenLoader::NotifyTPMTokenReady() {
296   FOR_EACH_OBSERVER(Observer, observers_, OnTPMTokenReady());
297 }
298 
LoggedInStateChanged()299 void TPMTokenLoader::LoggedInStateChanged() {
300   VLOG(1) << "LoggedInStateChanged";
301   MaybeStartTokenInitialization();
302 }
303 
304 }  // namespace chromeos
305