Home | History | Annotate | Download | only in chromeos
      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 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 
     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
     62 void TPMTokenLoader::Initialize() {
     63   CHECK(!g_tpm_token_loader);
     64   g_tpm_token_loader = new TPMTokenLoader(false /*for_test*/);
     65 }
     66 
     67 // static
     68 void TPMTokenLoader::InitializeForTest() {
     69   CHECK(!g_tpm_token_loader);
     70   g_tpm_token_loader = new TPMTokenLoader(true /*for_test*/);
     71 }
     72 
     73 // static
     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
     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
     88 bool TPMTokenLoader::IsInitialized() {
     89   return g_tpm_token_loader;
     90 }
     91 
     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 
    108 void TPMTokenLoader::SetCryptoTaskRunner(
    109     const scoped_refptr<base::SequencedTaskRunner>& crypto_task_runner) {
    110   crypto_task_runner_ = crypto_task_runner;
    111   MaybeStartTokenInitialization();
    112 }
    113 
    114 TPMTokenLoader::~TPMTokenLoader() {
    115   if (!initialized_for_test_ && LoginState::IsInitialized())
    116     LoginState::Get()->RemoveObserver(this);
    117 }
    118 
    119 void TPMTokenLoader::AddObserver(TPMTokenLoader::Observer* observer) {
    120   observers_.AddObserver(observer);
    121 }
    122 
    123 void TPMTokenLoader::RemoveObserver(TPMTokenLoader::Observer* observer) {
    124   observers_.RemoveObserver(observer);
    125 }
    126 
    127 bool TPMTokenLoader::IsTPMTokenReady() const {
    128   return tpm_token_state_ == TPM_DISABLED ||
    129          tpm_token_state_ == TPM_TOKEN_INITIALIZED;
    130 }
    131 
    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 
    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 
    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 
    235 void TPMTokenLoader::OnPersistentNSSDBOpened() {
    236   VLOG(1) << "PersistentNSSDBOpened";
    237   tpm_token_state_ = TPM_DB_OPENED;
    238   ContinueTokenInitialization();
    239 }
    240 
    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 
    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 
    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 
    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 
    295 void TPMTokenLoader::NotifyTPMTokenReady() {
    296   FOR_EACH_OBSERVER(Observer, observers_, OnTPMTokenReady());
    297 }
    298 
    299 void TPMTokenLoader::LoggedInStateChanged() {
    300   VLOG(1) << "LoggedInStateChanged";
    301   MaybeStartTokenInitialization();
    302 }
    303 
    304 }  // namespace chromeos
    305