blob: 3c779a6e2a01c771ab1b39ed0377b034831e3b8c [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cryptohome/lockbox-cache-manager/lockbox-cache-manager.h"
#include <string>
#include <vector>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <brillo/file_utils.h>
#include <brillo/files/file_util.h>
#include <libhwsec-foundation/tpm/tpm_version.h>
namespace cryptohome {
namespace {
MigrationStatus CopyFileAtomic(const base::FilePath& from_path,
const base::FilePath& to_path,
mode_t mode) {
// Read the file contents to a string.
std::string contents;
if (!base::ReadFileToString(from_path, &contents)) {
LOG(ERROR) << "Failed to read from " << from_path.value();
return MigrationStatus::kReadFail;
}
// Write the contents to the destination path atomically.
if (!brillo::WriteToFileAtomic(to_path, contents.data(), contents.size(),
mode)) {
LOG(ERROR) << "Failed to write the contents from " << from_path.value()
<< " to " << to_path.value();
return MigrationStatus::kCopyFail;
}
// Just being extra careful, we sync the destination dir.
if (!brillo::SyncFileOrDirectory(to_path.DirName(),
/*is_directory=*/true,
/*data_sync*/ false)) {
LOG(ERROR) << "Failed to sync dir: " << to_path.DirName().value();
return MigrationStatus::kSyncFail;
}
LOG(INFO) << "Install-time attributes content migration successful";
return MigrationStatus::kSuccess;
}
} // namespace
LockboxCacheManager::LockboxCacheManager(const base::FilePath& root)
: metrics_(std::make_unique<Metrics>()),
platform_(std::make_unique<Platform>()),
root_(root) {
install_attrs_old_path_ = root_.Append(filepaths::kLocOldInstallAttrs);
install_attrs_new_path_ = root_.Append(filepaths::kLocNewInstallAttrs);
lockbox_cache_path_ = root_.Append(filepaths::kLocLockboxCache);
lockbox_nvram_file_path_ = root_.Append(filepaths::kLocLockboxNvram);
}
// TODO(b/312392273): call the underlying library directly instead of calling
// the binary
void LockboxCacheManager::InvokeLockboxCacheTool() {
std::string output;
std::vector<std::string> argv = {
"lockbox-cache", "--nvram=" + lockbox_nvram_file_path_.value(),
"--cache=" + lockbox_cache_path_.value(),
"--lockbox=" + install_attrs_new_path_.value()};
if (!platform_->GetAppOutputAndError(std::move(argv), &output)) {
LOG(WARNING) << output;
}
}
bool LockboxCacheManager::Run() {
// Only needed if tpm version is dynamic. Otherwise, no-op.
PopulateLockboxNvramFile();
auto migration_status = MigrateInstallAttributesIfNeeded();
metrics_->ReportInstallAttributesMigrationStatus(migration_status);
if (migration_status != MigrationStatus::kNotNeeded &&
migration_status != MigrationStatus::kSuccess) {
LOG(ERROR) << "Failed to migrate install-time attributes content!";
return false;
}
// Pre-work is done. It's time for validation and cache creation.
if (!CreateLockboxCache()) {
LOG(WARNING) << "Failed to create lockbox-cache";
return false;
}
// There are no other consumers of the nvram data, so remove it. DeleteFile()
// returns true even if the source path is absent, but throws an error that
// might be confusing. So we check the existence first.
if (base::PathExists(lockbox_nvram_file_path_) &&
!brillo::DeleteFile(lockbox_nvram_file_path_)) {
LOG(ERROR) << "Failed to remove the nvram file!";
}
return true;
}
MigrationStatus LockboxCacheManager::MigrateInstallAttributesIfNeeded() {
if (!base::PathExists(install_attrs_old_path_)) {
LOG(INFO) << "No legacy install-attributes is found";
return MigrationStatus::kNotNeeded;
}
LOG(INFO) << "Legacy install-attributes found. Attempting migration...";
return CopyFileAtomic(install_attrs_old_path_, install_attrs_new_path_, 0644);
}
#if USE_TPM_DYNAMIC
// TODO(b/312392273): call the underlying library directly instead of calling
// the binary
void LockboxCacheManager::PopulateLockboxNvramFile() {
// Use tpm_manager to read the NV space.
// Note: tpm_manager should be available at this stage.
static const char* kNvramIndex;
TPM_SELECT_BEGIN;
TPM1_SECTION({ kNvramIndex = "0x20000004"; });
TPM2_SECTION({ kNvramIndex = "0x9da5b0"; });
OTHER_TPM_SECTION({
LOG(ERROR) << "Unsupported TPM platform.";
kNvramIndex = "0x9da5b0";
});
TPM_SELECT_END;
std::string index = "--index=" + std::string(kNvramIndex);
std::string file = "--file=" + lockbox_nvram_file_path_.value();
std::string output;
if (!platform_->GetAppOutputAndError(
{"tpm_manager_client", "read_space", index, file}, &output)) {
LOG(WARNING) << "Failed to read nvram contents from nvram index: "
<< output;
}
}
bool LockboxCacheManager::CreateLockboxCache() {
if (!base::PathExists(lockbox_nvram_file_path_)) {
LOG(INFO) << "Missing " << lockbox_nvram_file_path_.value()
<< ", may be intended if "
"lockbox nvram contents are empty.";
return true;
}
int64_t file_size;
if (base::GetFileSize(lockbox_nvram_file_path_, &file_size) &&
file_size > 0) {
InvokeLockboxCacheTool();
return true;
}
// For TPM-less devices and legacy CR1 devices, pretend like lockbox is
// supported.
if (base::PathExists(install_attrs_new_path_)) {
return base::CopyFile(install_attrs_new_path_, lockbox_cache_path_);
}
return true;
}
#else
// no-op
void LockboxCacheManager::PopulateLockboxNvramFile() {}
bool LockboxCacheManager::CreateLockboxCache() {
if (!base::PathExists(lockbox_nvram_file_path_)) {
LOG(INFO) << "Missing " << lockbox_nvram_file_path_.value()
<< ", may be intended if "
"lockbox nvram contents are empty.";
return true;
}
if (platform_->IsOwnedByRoot(lockbox_nvram_file_path_.value())) {
InvokeLockboxCacheTool();
return true;
}
LOG(ERROR) << lockbox_nvram_file_path_.value() << " is not owned by root!";
return false;
}
#endif
} // namespace cryptohome