1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/tpm/tpm_token_info_getter.h"
#include <stdint.h>
#include <utility>
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/task/task_runner.h"
#include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
#include "chromeos/ash/components/tpm/buildflags.h"
#include "chromeos/dbus/tpm_manager/tpm_manager.pb.h"
#include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
namespace ash {
namespace {
const int64_t kInitialRequestDelayMs = 100;
const int64_t kMaxRequestDelayMs = 300000; // 5 minutes
#if BUILDFLAG(NSS_SLOTS_SOFTWARE_FALLBACK)
constexpr bool kIsSystemSlotSoftwareFallbackAllowed = true;
#else
constexpr bool kIsSystemSlotSoftwareFallbackAllowed = false;
#endif
// Calculates the delay before running next attempt to initiatialize the TPM
// token, if |last_delay| was the last or initial delay.
base::TimeDelta GetNextRequestDelayMs(base::TimeDelta last_delay) {
// This implements an exponential backoff, as we don't know in which order of
// magnitude the TPM token changes it's state.
base::TimeDelta next_delay = last_delay * 2;
// Cap the delay to prevent an overflow. This threshold is arbitrarily chosen.
const base::TimeDelta max_delay = base::Milliseconds(kMaxRequestDelayMs);
if (next_delay > max_delay)
next_delay = max_delay;
return next_delay;
}
} // namespace
// static
std::unique_ptr<TPMTokenInfoGetter> TPMTokenInfoGetter::CreateForUserToken(
const AccountId& account_id,
CryptohomePkcs11Client* userdataauth_client,
const scoped_refptr<base::TaskRunner>& delayed_task_runner) {
CHECK(account_id.is_valid());
return base::WrapUnique(new TPMTokenInfoGetter(
TYPE_USER, account_id, userdataauth_client, delayed_task_runner));
}
// static
std::unique_ptr<TPMTokenInfoGetter> TPMTokenInfoGetter::CreateForSystemToken(
CryptohomePkcs11Client* userdataauth_client,
const scoped_refptr<base::TaskRunner>& delayed_task_runner) {
return base::WrapUnique(new TPMTokenInfoGetter(
TYPE_SYSTEM, EmptyAccountId(), userdataauth_client, delayed_task_runner));
}
TPMTokenInfoGetter::~TPMTokenInfoGetter() = default;
void TPMTokenInfoGetter::Start(TpmTokenInfoCallback callback) {
CHECK(state_ == STATE_INITIAL);
CHECK(!callback.is_null());
callback_ = std::move(callback);
state_ = STATE_STARTED;
Continue();
}
TPMTokenInfoGetter::TPMTokenInfoGetter(
TPMTokenInfoGetter::Type type,
const AccountId& account_id,
CryptohomePkcs11Client* cryptohome_pkcs11_client,
const scoped_refptr<base::TaskRunner>& delayed_task_runner)
: delayed_task_runner_(delayed_task_runner),
type_(type),
state_(TPMTokenInfoGetter::STATE_INITIAL),
account_id_(account_id),
use_nss_slots_software_fallback_(kIsSystemSlotSoftwareFallbackAllowed),
tpm_request_delay_(base::Milliseconds(kInitialRequestDelayMs)),
cryptohome_pkcs11_client_(cryptohome_pkcs11_client) {}
void TPMTokenInfoGetter::Continue() {
user_data_auth::Pkcs11GetTpmTokenInfoRequest request;
switch (state_) {
case STATE_INITIAL:
NOTREACHED();
case STATE_STARTED:
chromeos::TpmManagerClient::Get()->GetTpmNonsensitiveStatus(
::tpm_manager::GetTpmNonsensitiveStatusRequest(),
base::BindOnce(&TPMTokenInfoGetter::OnGetTpmStatus,
weak_factory_.GetWeakPtr()));
break;
case STATE_TPM_ENABLED:
case STATE_NSS_SLOTS_SOFTWARE_FALLBACK:
// For system token, we don't need to supply the username, and with an
// empty username, cryptohomed will return the system token information.
if (type_ == TYPE_USER) {
request.set_username(
cryptohome::CreateAccountIdentifierFromAccountId(account_id_)
.account_id());
}
cryptohome_pkcs11_client_->Pkcs11GetTpmTokenInfo(
request, base::BindOnce(&TPMTokenInfoGetter::OnPkcs11GetTpmTokenInfo,
weak_factory_.GetWeakPtr()));
break;
case STATE_DONE:
NOTREACHED();
}
}
void TPMTokenInfoGetter::RetryLater() {
delayed_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&TPMTokenInfoGetter::Continue, weak_factory_.GetWeakPtr()),
tpm_request_delay_);
tpm_request_delay_ = GetNextRequestDelayMs(tpm_request_delay_);
}
void TPMTokenInfoGetter::OnGetTpmStatus(
const ::tpm_manager::GetTpmNonsensitiveStatusReply& reply) {
if (reply.status() != ::tpm_manager::STATUS_SUCCESS) {
LOG(WARNING) << "Failed to get tpm status; status: " << reply.status();
RetryLater();
return;
}
// In case the use_nss_slots_software_fallback_ is true and the TPM is not
// owned, we continue the token info retrieval for the nss slots in order to
// fall back to a software-backed initialization.
if (use_nss_slots_software_fallback_ && !reply.is_owned()) {
state_ = STATE_NSS_SLOTS_SOFTWARE_FALLBACK;
Continue();
return;
}
if (!reply.is_enabled()) {
state_ = STATE_DONE;
std::move(callback_).Run(std::nullopt);
return;
}
state_ = STATE_TPM_ENABLED;
Continue();
}
void TPMTokenInfoGetter::OnPkcs11GetTpmTokenInfo(
std::optional<user_data_auth::Pkcs11GetTpmTokenInfoReply> token_info) {
if (!token_info.has_value() || !token_info->has_token_info() ||
token_info->token_info().slot() == -1) {
RetryLater();
return;
}
state_ = STATE_DONE;
std::move(callback_).Run(token_info->token_info());
}
} // namespace ash
|