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. 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 41 void PostResultToTaskRunner(scoped_refptr<base::SequencedTaskRunner> runner, 42 const base::Callback<void(bool)>& callback, 43 bool success) { 44 runner->PostTask(FROM_HERE, base::Bind(callback, success)); 45 } 46 47 } // namespace 48 49 static TPMTokenLoader* g_tpm_token_loader = NULL; 50 51 // static 52 void TPMTokenLoader::Initialize() { 53 CHECK(!g_tpm_token_loader); 54 g_tpm_token_loader = new TPMTokenLoader(false /*for_test*/); 55 } 56 57 // static 58 void TPMTokenLoader::InitializeForTest() { 59 CHECK(!g_tpm_token_loader); 60 g_tpm_token_loader = new TPMTokenLoader(true /*for_test*/); 61 } 62 63 // static 64 void TPMTokenLoader::Shutdown() { 65 CHECK(g_tpm_token_loader); 66 delete g_tpm_token_loader; 67 g_tpm_token_loader = NULL; 68 } 69 70 // static 71 TPMTokenLoader* TPMTokenLoader::Get() { 72 CHECK(g_tpm_token_loader) 73 << "TPMTokenLoader::Get() called before Initialize()"; 74 return g_tpm_token_loader; 75 } 76 77 // static 78 bool TPMTokenLoader::IsInitialized() { 79 return g_tpm_token_loader; 80 } 81 82 TPMTokenLoader::TPMTokenLoader(bool for_test) 83 : initialized_for_test_(for_test), 84 tpm_token_state_(TPM_STATE_UNKNOWN), 85 tpm_request_delay_( 86 base::TimeDelta::FromMilliseconds(kInitialRequestDelayMs)), 87 tpm_token_slot_id_(-1), 88 weak_factory_(this) { 89 if (!initialized_for_test_ && LoginState::IsInitialized()) 90 LoginState::Get()->AddObserver(this); 91 92 if (initialized_for_test_) { 93 tpm_token_state_ = TPM_TOKEN_INITIALIZED; 94 tpm_user_pin_ = "111111"; 95 } 96 } 97 98 void TPMTokenLoader::SetCryptoTaskRunner( 99 const scoped_refptr<base::SequencedTaskRunner>& crypto_task_runner) { 100 crypto_task_runner_ = crypto_task_runner; 101 MaybeStartTokenInitialization(); 102 } 103 104 TPMTokenLoader::~TPMTokenLoader() { 105 if (!initialized_for_test_ && LoginState::IsInitialized()) 106 LoginState::Get()->RemoveObserver(this); 107 } 108 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; 119 } 120 121 bool TPMTokenLoader::IsTPMLoadingEnabled() const { 122 // TPM loading is enabled on non-ChromeOS environments, e.g. when running 123 // tests on Linux. 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()); 127 } 128 129 void TPMTokenLoader::MaybeStartTokenInitialization() { 130 CHECK(thread_checker_.CalledOnValidThread()); 131 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()) 135 return; 136 137 if (!LoginState::IsInitialized()) 138 return; 139 140 bool start_initialization = LoginState::Get()->IsUserLoggedIn(); 141 142 VLOG(1) << "StartTokenInitialization: " << start_initialization; 143 if (!start_initialization) 144 return; 145 146 if (!IsTPMLoadingEnabled()) 147 tpm_token_state_ = TPM_DISABLED; 148 149 ContinueTokenInitialization(); 150 151 DCHECK_NE(tpm_token_state_, TPM_STATE_UNKNOWN); 152 } 153 154 void TPMTokenLoader::ContinueTokenInitialization() { 155 CHECK(thread_checker_.CalledOnValidThread()); 156 VLOG(1) << "ContinueTokenInitialization: " << tpm_token_state_; 157 158 switch (tpm_token_state_) { 159 case TPM_STATE_UNKNOWN: { 160 crypto_task_runner_->PostTaskAndReply( 161 FROM_HERE, 162 base::Bind(&crypto::EnableTPMTokenForNSS), 163 base::Bind(&TPMTokenLoader::OnTPMTokenEnabledForNSS, 164 weak_factory_.GetWeakPtr())); 165 tpm_token_state_ = TPM_INITIALIZATION_STARTED; 166 return; 167 } 168 case TPM_INITIALIZATION_STARTED: { 169 NOTREACHED(); 170 return; 171 } 172 case TPM_TOKEN_ENABLED_FOR_NSS: { 173 DBusThreadManager::Get()->GetCryptohomeClient()->TpmIsEnabled( 174 base::Bind(&TPMTokenLoader::OnTpmIsEnabled, 175 weak_factory_.GetWeakPtr())); 176 return; 177 } 178 case TPM_DISABLED: { 179 // TPM is disabled, so proceed with empty tpm token name. 180 NotifyTPMTokenReady(); 181 return; 182 } 183 case TPM_ENABLED: { 184 DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11IsTpmTokenReady( 185 base::Bind(&TPMTokenLoader::OnPkcs11IsTpmTokenReady, 186 weak_factory_.GetWeakPtr())); 187 return; 188 } 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())); 195 return; 196 } 197 case TPM_TOKEN_INFO_RECEIVED: { 198 crypto_task_runner_->PostTask( 199 FROM_HERE, 200 base::Bind( 201 &crypto::InitializeTPMTokenAndSystemSlot, 202 tpm_token_slot_id_, 203 base::Bind(&PostResultToTaskRunner, 204 base::MessageLoopProxy::current(), 205 base::Bind(&TPMTokenLoader::OnTPMTokenInitialized, 206 weak_factory_.GetWeakPtr())))); 207 return; 208 } 209 case TPM_TOKEN_INITIALIZED: { 210 NotifyTPMTokenReady(); 211 return; 212 } 213 } 214 } 215 216 void TPMTokenLoader::RetryTokenInitializationLater() { 217 CHECK(thread_checker_.CalledOnValidThread()); 218 VLOG(1) << "Retry token initialization later."; 219 base::MessageLoopProxy::current()->PostDelayedTask( 220 FROM_HERE, 221 base::Bind(&TPMTokenLoader::ContinueTokenInitialization, 222 weak_factory_.GetWeakPtr()), 223 tpm_request_delay_); 224 tpm_request_delay_ = GetNextRequestDelayMs(tpm_request_delay_); 225 } 226 227 void TPMTokenLoader::OnTPMTokenEnabledForNSS() { 228 VLOG(1) << "TPMTokenEnabledForNSS"; 229 tpm_token_state_ = TPM_TOKEN_ENABLED_FOR_NSS; 230 ContinueTokenInitialization(); 231 } 232 233 void TPMTokenLoader::OnTpmIsEnabled(DBusMethodCallStatus call_status, 234 bool tpm_is_enabled) { 235 VLOG(1) << "OnTpmIsEnabled: " << tpm_is_enabled; 236 237 if (call_status == DBUS_METHOD_CALL_SUCCESS && tpm_is_enabled) 238 tpm_token_state_ = TPM_ENABLED; 239 else 240 tpm_token_state_ = TPM_DISABLED; 241 242 ContinueTokenInitialization(); 243 } 244 245 void TPMTokenLoader::OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status, 246 bool is_tpm_token_ready) { 247 VLOG(1) << "OnPkcs11IsTpmTokenReady: " << is_tpm_token_ready; 248 249 if (call_status == DBUS_METHOD_CALL_FAILURE || !is_tpm_token_ready) { 250 RetryTokenInitializationLater(); 251 return; 252 } 253 254 tpm_token_state_ = TPM_TOKEN_READY; 255 ContinueTokenInitialization(); 256 } 257 258 void TPMTokenLoader::OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status, 259 const std::string& token_name, 260 const std::string& user_pin, 261 int token_slot_id) { 262 VLOG(1) << "OnPkcs11GetTpmTokenInfo: " << token_name; 263 264 if (call_status == DBUS_METHOD_CALL_FAILURE) { 265 RetryTokenInitializationLater(); 266 return; 267 } 268 269 tpm_token_slot_id_ = token_slot_id; 270 tpm_user_pin_ = user_pin; 271 tpm_token_state_ = TPM_TOKEN_INFO_RECEIVED; 272 273 ContinueTokenInitialization(); 274 } 275 276 void TPMTokenLoader::OnTPMTokenInitialized(bool success) { 277 VLOG(1) << "OnTPMTokenInitialized: " << success; 278 if (!success) { 279 RetryTokenInitializationLater(); 280 return; 281 } 282 tpm_token_state_ = TPM_TOKEN_INITIALIZED; 283 ContinueTokenInitialization(); 284 } 285 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(); 292 ++i) { 293 i->Run(tpm_status); 294 } 295 tpm_ready_callback_list_.clear(); 296 } 297 298 void TPMTokenLoader::LoggedInStateChanged() { 299 VLOG(1) << "LoggedInStateChanged"; 300 MaybeStartTokenInitialization(); 301 } 302 303 } // namespace chromeos 304