blob: 07852004fe936b2a4956f3c6d6032cd0f62babec [file] [log] [blame]
// Copyright 2017 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.
#include "chrome/browser/chromeos/tpm_firmware_update.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/important_file_writer.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_path_override.h"
#include "base/test/scoped_task_environment.h"
#include "base/values.h"
#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/chromeos/settings/stub_install_attributes.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/settings/cros_settings_names.h"
#include "chromeos/system/fake_statistics_provider.h"
#include "components/policy/proto/chrome_device_policy.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace tpm_firmware_update {
TEST(TPMFirmwareUpdateTest, DecodeSettingsProto) {
enterprise_management::TPMFirmwareUpdateSettingsProto settings;
settings.set_allow_user_initiated_powerwash(true);
settings.set_allow_user_initiated_preserve_device_state(true);
auto dict = DecodeSettingsProto(settings);
ASSERT_TRUE(dict);
bool value = false;
EXPECT_TRUE(dict->GetBoolean("allow-user-initiated-powerwash", &value));
EXPECT_TRUE(value);
EXPECT_TRUE(
dict->GetBoolean("allow-user-initiated-preserve-device-state", &value));
EXPECT_TRUE(value);
}
class TPMFirmwareUpdateModesTest : public testing::Test {
public:
enum class Availability {
kPending,
kUnavailable,
kUnavailableROCAVulnerable,
kAvailable,
};
TPMFirmwareUpdateModesTest() = default;
void SetUp() override {
feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
feature_list_->InitAndEnableFeature(features::kTPMFirmwareUpdate);
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
base::FilePath update_location_path =
temp_dir_.GetPath().AppendASCII("tpm_firmware_update_location");
path_override_location_ = std::make_unique<base::ScopedPathOverride>(
chrome::FILE_CHROME_OS_TPM_FIRMWARE_UPDATE_LOCATION,
update_location_path, update_location_path.IsAbsolute(), false);
base::FilePath srk_vulnerable_roca_path = temp_dir_.GetPath().AppendASCII(
"tpm_firmware_update_srk_vulnerable_roca");
path_override_srk_vulnerable_roca_ =
std::make_unique<base::ScopedPathOverride>(
chrome::FILE_CHROME_OS_TPM_FIRMWARE_UPDATE_SRK_VULNERABLE_ROCA,
srk_vulnerable_roca_path, srk_vulnerable_roca_path.IsAbsolute(),
false);
SetUpdateAvailability(Availability::kAvailable);
callback_ = base::BindOnce(&TPMFirmwareUpdateModesTest::RecordResponse,
base::Unretained(this));
}
void RecordResponse(const std::set<Mode>& modes) {
callback_received_ = true;
callback_modes_ = modes;
}
void SetUpdateAvailability(Availability availability) {
base::FilePath srk_vulnerable_roca_path;
ASSERT_TRUE(base::PathService::Get(
chrome::FILE_CHROME_OS_TPM_FIRMWARE_UPDATE_SRK_VULNERABLE_ROCA,
&srk_vulnerable_roca_path));
switch (availability) {
case Availability::kPending:
case Availability::kUnavailable:
base::DeleteFile(srk_vulnerable_roca_path, false);
break;
case Availability::kAvailable:
case Availability::kUnavailableROCAVulnerable:
ASSERT_TRUE(base::ImportantFileWriter::WriteFileAtomically(
srk_vulnerable_roca_path, ""));
break;
}
base::FilePath update_location_path;
ASSERT_TRUE(base::PathService::Get(
chrome::FILE_CHROME_OS_TPM_FIRMWARE_UPDATE_LOCATION,
&update_location_path));
switch (availability) {
case Availability::kPending:
base::DeleteFile(update_location_path, false);
break;
case Availability::kUnavailable:
case Availability::kUnavailableROCAVulnerable:
ASSERT_TRUE(base::ImportantFileWriter::WriteFileAtomically(
update_location_path, ""));
break;
case Availability::kAvailable:
const char kUpdatePath[] = "/lib/firmware/tpm/firmware.bin";
ASSERT_TRUE(base::ImportantFileWriter::WriteFileAtomically(
update_location_path, kUpdatePath));
break;
}
}
std::unique_ptr<base::test::ScopedFeatureList> feature_list_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<base::ScopedPathOverride> path_override_location_;
std::unique_ptr<base::ScopedPathOverride> path_override_srk_vulnerable_roca_;
base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
ScopedCrosSettingsTestHelper cros_settings_test_helper_;
chromeos::system::ScopedFakeStatisticsProvider statistics_provider_;
const std::set<Mode> kAllModes{Mode::kPowerwash, Mode::kPreserveDeviceState};
bool callback_received_ = false;
std::set<Mode> callback_modes_;
base::OnceCallback<void(const std::set<Mode>&)> callback_;
};
TEST_F(TPMFirmwareUpdateModesTest, FeatureDisabled) {
feature_list_.reset();
feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
feature_list_->InitAndDisableFeature(features::kTPMFirmwareUpdate);
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
EXPECT_TRUE(callback_received_);
EXPECT_TRUE(callback_modes_.empty());
}
TEST_F(TPMFirmwareUpdateModesTest, FRERequired) {
statistics_provider_.SetMachineStatistic(system::kCheckEnrollmentKey, "1");
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
EXPECT_TRUE(callback_received_);
EXPECT_TRUE(callback_modes_.empty());
}
TEST_F(TPMFirmwareUpdateModesTest, Pending) {
SetUpdateAvailability(Availability::kPending);
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_TRUE(callback_modes_.empty());
}
TEST_F(TPMFirmwareUpdateModesTest, Available) {
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_EQ(kAllModes, callback_modes_);
}
TEST_F(TPMFirmwareUpdateModesTest, AvailableAfterWaiting) {
SetUpdateAvailability(Availability::kPending);
GetAvailableUpdateModes(std::move(callback_),
base::TimeDelta::FromSeconds(5));
scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(callback_received_);
// When testing that file appearance triggers the callback, we can't rely on
// a single execution of ScopedTaskEnvironment::RunUntilIdle(). This is
// because ScopedTaskEnvironment doesn't know about file system events that
// haven't fired and propagated to a task scheduler thread yet so may return
// early before the file system event is received. An event is expected here
// though, so keep spinning the loop until the callback is received. This
// isn't ideal, but better than flakiness due to file system events racing
// with a single invocation of RunUntilIdle().
SetUpdateAvailability(Availability::kAvailable);
while (!callback_received_) {
scoped_task_environment_.RunUntilIdle();
}
EXPECT_EQ(kAllModes, callback_modes_);
// Trigger timeout and validate there are no further callbacks or crashes.
callback_received_ = false;
scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(5));
scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(callback_received_);
}
TEST_F(TPMFirmwareUpdateModesTest, NoUpdateVulnerableSRK) {
SetUpdateAvailability(Availability::kUnavailableROCAVulnerable);
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_EQ(std::set<Mode>{Mode::kCleanup}, callback_modes_);
}
TEST_F(TPMFirmwareUpdateModesTest, NoUpdateNonVulnerableSRK) {
SetUpdateAvailability(Availability::kUnavailable);
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_EQ(std::set<Mode>(), callback_modes_);
}
TEST_F(TPMFirmwareUpdateModesTest, Timeout) {
SetUpdateAvailability(Availability::kPending);
GetAvailableUpdateModes(std::move(callback_),
base::TimeDelta::FromSeconds(5));
scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(callback_received_);
scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(5));
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_TRUE(callback_modes_.empty());
}
class TPMFirmwareUpdateModesEnterpriseTest : public TPMFirmwareUpdateModesTest {
public:
void SetUp() override {
TPMFirmwareUpdateModesTest::SetUp();
cros_settings_test_helper_.ReplaceDeviceSettingsProviderWithStub();
cros_settings_test_helper_.InstallAttributes()->SetCloudManaged(
"example.com", "fake-device-id");
}
void SetPolicy(const std::set<Mode>& modes) {
base::DictionaryValue dict;
dict.SetKey(kSettingsKeyAllowPowerwash,
base::Value(modes.count(Mode::kPowerwash) > 0));
dict.SetKey(kSettingsKeyAllowPreserveDeviceState,
base::Value(modes.count(Mode::kPreserveDeviceState) > 0));
cros_settings_test_helper_.Set(kTPMFirmwareUpdateSettings, dict);
}
};
TEST_F(TPMFirmwareUpdateModesEnterpriseTest, DeviceSettingPending) {
SetPolicy(kAllModes);
cros_settings_test_helper_.SetTrustedStatus(
CrosSettingsProvider::TEMPORARILY_UNTRUSTED);
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(callback_received_);
cros_settings_test_helper_.SetTrustedStatus(CrosSettingsProvider::TRUSTED);
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_EQ(kAllModes, callback_modes_);
}
TEST_F(TPMFirmwareUpdateModesEnterpriseTest, DeviceSettingUntrusted) {
cros_settings_test_helper_.SetTrustedStatus(
CrosSettingsProvider::PERMANENTLY_UNTRUSTED);
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_TRUE(callback_modes_.empty());
}
TEST_F(TPMFirmwareUpdateModesEnterpriseTest, DeviceSettingNotSet) {
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_TRUE(callback_modes_.empty());
}
TEST_F(TPMFirmwareUpdateModesEnterpriseTest, DeviceSettingDisallowed) {
SetPolicy({});
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_TRUE(callback_modes_.empty());
}
TEST_F(TPMFirmwareUpdateModesEnterpriseTest, DeviceSettingPowerwashAllowed) {
SetPolicy({Mode::kPowerwash});
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_EQ(std::set<Mode>({Mode::kPowerwash}), callback_modes_);
}
TEST_F(TPMFirmwareUpdateModesEnterpriseTest,
DeviceSettingPreserveDeviceStateAllowed) {
SetPolicy({Mode::kPreserveDeviceState});
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_EQ(std::set<Mode>({Mode::kPreserveDeviceState}), callback_modes_);
}
TEST_F(TPMFirmwareUpdateModesEnterpriseTest, VulnerableSRK) {
SetUpdateAvailability(Availability::kUnavailableROCAVulnerable);
SetPolicy({Mode::kPreserveDeviceState});
GetAvailableUpdateModes(std::move(callback_), base::TimeDelta());
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_received_);
EXPECT_EQ(std::set<Mode>({Mode::kCleanup}), callback_modes_);
}
} // namespace tpm_firmware_update
} // namespace chromeos