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 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