Commit 637f4404 authored by Maria Petrisor's avatar Maria Petrisor Committed by Chromium LUCI CQ

Add software fallback for system token

The fallback will be enabled on Neverware devices using the added build
flag: system_slot_software_fallback. Its default value is false and this
behavior is not intended for Chromebooks which always have a TPM.

In case Neverware enables the build flag:
  (a) Devices with a TPM will still initialize a TPM-backed system slot.
  (b) Devices without a TPM will initialize a software-backed system
      slot.

DD: https://docs.google.com/document/d/1rbTiAMpR18SoN0be_AW-ysvptAHbYcTYeZHL6KcAcTQ/edit?usp=sharing
Initial bug discussion: https://docs.google.com/document/d/16w4xyCi2tQTIBtS8o4Ga01KxpHwICVnCjyGC6ezlNS0/edit?usp=sharing

Bug: b:171461831
Change-Id: I533a7de7bf5367143aa539b9a7883abd6d8f405a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2552600
Commit-Queue: Maria Petrisor <mpetrisor@chromium.org>
Reviewed-by: default avatarMattias Nissler <mnissler@chromium.org>
Reviewed-by: default avatarAlexander Hendrich <hendrich@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Reviewed-by: default avatarAchuith Bhandarkar <achuith@chromium.org>
Cr-Commit-Position: refs/heads/master@{#833385}
parent 3ace4f37
......@@ -235,6 +235,7 @@ source_set("chromeos") {
"//chromeos/system",
"//chromeos/timezone",
"//chromeos/tpm",
"//chromeos/tpm:buildflags",
"//chromeos/ui/base",
"//chromeos/ui/frame",
"//components/arc",
......
......@@ -13,6 +13,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/sequence_checker.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/branding_buildflags.h"
#include "build/buildflag.h"
......@@ -21,6 +22,7 @@
#include "chromeos/dbus/tpm_manager/tpm_manager.pb.h"
#include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
#include "chromeos/network/network_cert_loader.h"
#include "chromeos/tpm/buildflags.h"
#include "chromeos/tpm/tpm_token_loader.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
......@@ -32,6 +34,10 @@ namespace chromeos {
namespace {
constexpr base::TimeDelta kInitialRequestDelay =
base::TimeDelta::FromMilliseconds(100);
constexpr base::TimeDelta kMaxRequestDelay = base::TimeDelta::FromMinutes(5);
// Called on UI Thread when the system slot has been retrieved.
void GotSystemSlotOnUIThread(
base::OnceCallback<void(crypto::ScopedPK11Slot)> callback_ui_thread,
......@@ -77,6 +83,24 @@ bool ShallAttemptTpmOwnership() {
#endif
}
// Checks if the build flag system_slot_software_fallback is enabled.
bool IsSystemSlotSoftwareFallbackEnabled() {
#if BUILDFLAG(SYSTEM_SLOT_SOFTWARE_FALLBACK)
return true;
#else
return false;
#endif
}
// Calculates the delay before running next attempt to get the TPM state
// (enabled/disabled), if |last_delay| was the last or initial delay.
base::TimeDelta GetNextRequestDelay(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. The delay is capped to prevent
// overflow. This threshold is arbitrarily chosen.
return std::min(last_delay * 2, kMaxRequestDelay);
}
// ChromeBrowserMainPartsChromeos owns this.
SystemTokenCertDBInitializer* g_system_token_cert_db_initializer = nullptr;
......@@ -85,7 +109,8 @@ SystemTokenCertDBInitializer* g_system_token_cert_db_initializer = nullptr;
constexpr base::TimeDelta
SystemTokenCertDBInitializer::kMaxCertDbRetrievalDelay;
SystemTokenCertDBInitializer::SystemTokenCertDBInitializer() {
SystemTokenCertDBInitializer::SystemTokenCertDBInitializer()
: tpm_request_delay_(kInitialRequestDelay) {
// Only start loading the system token once cryptohome is available and only
// if the TPM is ready (available && owned && not being owned).
CryptohomeClient::Get()->WaitForServiceToBeAvailable(
......@@ -177,6 +202,50 @@ void SystemTokenCertDBInitializer::OnCryptohomeAvailable(bool available) {
VLOG(1) << "SystemTokenCertDBInitializer: Cryptohome available.";
TpmManagerClient::Get()->AddObserver(this);
CheckTpm();
}
void SystemTokenCertDBInitializer::CheckTpm() {
if (IsSystemSlotSoftwareFallbackEnabled()) {
TpmManagerClient::Get()->GetTpmNonsensitiveStatus(
::tpm_manager::GetTpmNonsensitiveStatusRequest(),
base::BindOnce(&SystemTokenCertDBInitializer::OnGetTpmStatus,
weak_ptr_factory_.GetWeakPtr()));
} else {
CryptohomeClient::Get()->TpmIsReady(
base::BindOnce(&SystemTokenCertDBInitializer::OnGotTpmIsReady,
weak_ptr_factory_.GetWeakPtr()));
}
}
void SystemTokenCertDBInitializer::RetryCheckTpmLater() {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&SystemTokenCertDBInitializer::CheckTpm,
weak_ptr_factory_.GetWeakPtr()),
tpm_request_delay_);
tpm_request_delay_ = GetNextRequestDelay(tpm_request_delay_);
}
void SystemTokenCertDBInitializer::OnGetTpmStatus(
const ::tpm_manager::GetTpmNonsensitiveStatusReply& reply) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (reply.status() != ::tpm_manager::STATUS_SUCCESS) {
LOG(WARNING) << "Failed to get tpm status; status: " << reply.status();
RetryCheckTpmLater();
return;
}
// When the software fallback flag is set and the TPM is disabled, we skip the
// TpmIsReady() call. Otherwise, because the TPM won't be ready and will never
// be signaled as such, we won't proceed to the database initialization.
if (!reply.is_enabled()) {
MaybeStartInitializingDatabase();
return;
}
CryptohomeClient::Get()->TpmIsReady(
base::BindOnce(&SystemTokenCertDBInitializer::OnGotTpmIsReady,
weak_ptr_factory_.GetWeakPtr()));
......
......@@ -19,6 +19,7 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/dbus/cryptohome/cryptohome_client.h"
#include "chromeos/dbus/tpm_manager/tpm_manager.pb.h"
#include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
#include "crypto/scoped_nss_types.h"
......@@ -86,6 +87,22 @@ class SystemTokenCertDBInitializer : public TpmManagerClient::Observer {
// Called once the cryptohome service is available.
void OnCryptohomeAvailable(bool available);
// Verifies the value of the build flag system_slot_software_fallback and
// decides the initialization flow based on that.
void CheckTpm();
// If GetTpmNonsensitiveStatus() fails (e.g. if TPM token is not yet ready)
// schedules the initialization step retry attempt after a timeout.
void RetryCheckTpmLater();
// This is a callback for the GetTpmNonsensitiveStatus() query. It is only
// called when the build flag system_slot_software_fallback is enabled. If the
// build flag is enabled and TPM is disabled, we skip the cryptohome
// TpmIsReady() check during initialization, otherwise we continue the normal
// flow with TpmIsReady() and its callback.
void OnGetTpmStatus(
const ::tpm_manager::GetTpmNonsensitiveStatusReply& reply);
// This is a callback for the cryptohome TpmIsReady query. Note that this is
// not a listener which would be called once TPM becomes ready if it was not
// ready on startup - that event is observed by `OnOwnershipTakenSignal()`.
......@@ -124,6 +141,10 @@ class SystemTokenCertDBInitializer : public TpmManagerClient::Observer {
base::OneShotTimer system_token_cert_db_retrieval_timer_;
// The current request delay before the next attempt to retrieve the TPM
// state. Will be adapted after each attempt.
base::TimeDelta tpm_request_delay_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<SystemTokenCertDBInitializer> weak_ptr_factory_{this};
......
......@@ -2,13 +2,21 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/buildflag_header.gni")
import("//chromeos/tpm/buildflags.gni")
import("//testing/test.gni")
assert(is_chromeos, "Non-Chrome-OS builds must not depend on //chromeos")
buildflag_header("buildflags") {
header = "buildflags.h"
flags = [ "SYSTEM_SLOT_SOFTWARE_FALLBACK=$system_slot_software_fallback" ]
}
component("tpm") {
defines = [ "IS_CHROMEOS_TPM_IMPL" ]
deps = [
":buildflags",
"//base",
"//chromeos/cryptohome",
"//chromeos/dbus/constants",
......
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
declare_args() {
# True if the system slot should fall back to software-backed initialization
# in case of a disabled TPM.
system_slot_software_fallback = false
}
......@@ -71,6 +71,11 @@ void TPMTokenInfoGetter::Start(TpmTokenInfoCallback callback) {
Continue();
}
void TPMTokenInfoGetter::SetSystemSlotSoftwareFallback(
bool use_system_slot_software_fallback) {
use_system_slot_software_fallback_ = use_system_slot_software_fallback;
}
TPMTokenInfoGetter::TPMTokenInfoGetter(
TPMTokenInfoGetter::Type type,
const AccountId& account_id,
......@@ -107,6 +112,15 @@ void TPMTokenInfoGetter::Continue() {
weak_factory_.GetWeakPtr()));
}
break;
case STATE_SYSTEM_SLOT_SOFTWARE_FALLBACK:
if (type_ == TYPE_SYSTEM) {
cryptohome_client_->Pkcs11GetTpmTokenInfo(
base::BindOnce(&TPMTokenInfoGetter::OnPkcs11GetTpmTokenInfo,
weak_factory_.GetWeakPtr()));
} else { // if (type_ == TYPE_USER)
NOTREACHED();
}
break;
case STATE_DONE:
NOTREACHED();
}
......@@ -129,6 +143,15 @@ void TPMTokenInfoGetter::OnGetTpmStatus(
}
if (!reply.is_enabled()) {
// In case the TPM is disabled and use_system_slot_software_fallback_ is
// true, we continue the token info retrieval for the system slot in order
// to fall back to a software-backed initialization.
if (use_system_slot_software_fallback_) {
state_ = STATE_SYSTEM_SLOT_SOFTWARE_FALLBACK;
Continue();
return;
}
state_ = STATE_DONE;
std::move(callback_).Run(base::nullopt);
return;
......
......@@ -52,6 +52,8 @@ class COMPONENT_EXPORT(CHROMEOS_TPM) TPMTokenInfoGetter {
// called).
void Start(TpmTokenInfoCallback callback);
void SetSystemSlotSoftwareFallback(bool use_system_slot_software_fallback);
private:
enum Type {
TYPE_SYSTEM,
......@@ -62,6 +64,7 @@ class COMPONENT_EXPORT(CHROMEOS_TPM) TPMTokenInfoGetter {
STATE_INITIAL,
STATE_STARTED,
STATE_TPM_ENABLED,
STATE_SYSTEM_SLOT_SOFTWARE_FALLBACK,
STATE_DONE
};
......@@ -100,6 +103,12 @@ class COMPONENT_EXPORT(CHROMEOS_TPM) TPMTokenInfoGetter {
TpmTokenInfoCallback callback_;
// If set and the TPM is disabled, TPMTokenInfoGetter will still get the token
// info using cryptohome's Pkcs11GetTpmTokenInfo query. The token info is
// needed for falling back to a software-backed initialization of the system
// token.
bool use_system_slot_software_fallback_ = false;
// The current request delay before the next attempt to initialize the
// TPM. Will be adapted after each attempt.
base::TimeDelta tpm_request_delay_;
......
......@@ -292,6 +292,32 @@ TEST_F(SystemTPMTokenInfoGetterTest, TPMNotEnabled) {
EXPECT_EQ(std::vector<int64_t>(), delays_);
}
TEST_F(SystemTPMTokenInfoGetterTest, TPMNotEnabledSystemSlotFallbackEnabled) {
chromeos::TpmManagerClient::Get()
->GetTestInterface()
->mutable_nonsensitive_status_reply()
->set_is_enabled(false);
bool completed = false;
base::Optional<TpmTokenInfo> result;
tpm_token_info_getter_->SetSystemSlotSoftwareFallback(true);
tpm_token_info_getter_->Start(
base::BindOnce(&OnTpmTokenInfoGetterCompleted, &completed, &result));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(completed);
const TpmTokenInfo fake_token_info = {"TOKEN_1", "2222", 1};
cryptohome_client_->SetTpmTokenInfo(fake_token_info);
EXPECT_TRUE(completed);
ASSERT_TRUE(result.has_value());
EXPECT_EQ("TOKEN_1", result->label);
EXPECT_EQ("2222", result->user_pin);
EXPECT_EQ(1, result->slot);
EXPECT_EQ(std::vector<int64_t>(), delays_);
}
TEST_F(SystemTPMTokenInfoGetterTest, TpmEnabledCallFails) {
chromeos::TpmManagerClient::Get()
->GetTestInterface()
......
......@@ -14,6 +14,7 @@
#include "base/system/sys_info.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chromeos/tpm/buildflags.h"
#include "chromeos/tpm/tpm_token_info_getter.h"
#include "crypto/nss_util.h"
......@@ -27,6 +28,15 @@ void PostResultToTaskRunner(scoped_refptr<base::SequencedTaskRunner> runner,
runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback), success));
}
// Checks if the build flag system_slot_software_fallback is enabled.
bool IsSystemSlotSoftwareFallbackEnabled() {
#if BUILDFLAG(SYSTEM_SLOT_SOFTWARE_FALLBACK)
return true;
#else
return false;
#endif
}
} // namespace
static TPMTokenLoader* g_tpm_token_loader = NULL;
......@@ -70,6 +80,9 @@ TPMTokenLoader::TPMTokenLoader(bool initialized_for_test)
base::ThreadTaskRunnerHandle::Get())),
tpm_token_slot_id_(-1),
can_start_before_login_(false) {
tpm_token_info_getter_->SetSystemSlotSoftwareFallback(
IsSystemSlotSoftwareFallbackEnabled());
if (!initialized_for_test_ && LoginState::IsInitialized())
LoginState::Get()->AddObserver(this);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment