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 19namespace chromeos { 20 21namespace { 22 23const int64 kInitialRequestDelayMs = 100; 24const 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. 28base::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 41void 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 51void 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 59static TPMTokenLoader* g_tpm_token_loader = NULL; 60 61// static 62void TPMTokenLoader::Initialize() { 63 CHECK(!g_tpm_token_loader); 64 g_tpm_token_loader = new TPMTokenLoader(false /*for_test*/); 65} 66 67// static 68void TPMTokenLoader::InitializeForTest() { 69 CHECK(!g_tpm_token_loader); 70 g_tpm_token_loader = new TPMTokenLoader(true /*for_test*/); 71} 72 73// static 74void TPMTokenLoader::Shutdown() { 75 CHECK(g_tpm_token_loader); 76 delete g_tpm_token_loader; 77 g_tpm_token_loader = NULL; 78} 79 80// static 81TPMTokenLoader* TPMTokenLoader::Get() { 82 CHECK(g_tpm_token_loader) 83 << "TPMTokenLoader::Get() called before Initialize()"; 84 return g_tpm_token_loader; 85} 86 87// static 88bool TPMTokenLoader::IsInitialized() { 89 return g_tpm_token_loader; 90} 91 92TPMTokenLoader::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 108void TPMTokenLoader::SetCryptoTaskRunner( 109 const scoped_refptr<base::SequencedTaskRunner>& crypto_task_runner) { 110 crypto_task_runner_ = crypto_task_runner; 111 MaybeStartTokenInitialization(); 112} 113 114TPMTokenLoader::~TPMTokenLoader() { 115 if (!initialized_for_test_ && LoginState::IsInitialized()) 116 LoginState::Get()->RemoveObserver(this); 117} 118 119void TPMTokenLoader::AddObserver(TPMTokenLoader::Observer* observer) { 120 observers_.AddObserver(observer); 121} 122 123void TPMTokenLoader::RemoveObserver(TPMTokenLoader::Observer* observer) { 124 observers_.RemoveObserver(observer); 125} 126 127bool TPMTokenLoader::IsTPMTokenReady() const { 128 return tpm_token_state_ == TPM_DISABLED || 129 tpm_token_state_ == TPM_TOKEN_INITIALIZED; 130} 131 132void 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 162void 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 224void 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 235void TPMTokenLoader::OnPersistentNSSDBOpened() { 236 VLOG(1) << "PersistentNSSDBOpened"; 237 tpm_token_state_ = TPM_DB_OPENED; 238 ContinueTokenInitialization(); 239} 240 241void 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 253void 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 266void 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 285void 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 295void TPMTokenLoader::NotifyTPMTokenReady() { 296 FOR_EACH_OBSERVER(Observer, observers_, OnTPMTokenReady()); 297} 298 299void TPMTokenLoader::LoggedInStateChanged() { 300 VLOG(1) << "LoggedInStateChanged"; 301 MaybeStartTokenInitialization(); 302} 303 304} // namespace chromeos 305