blob: 4045844b224a477bb135289a91b4b68faccc5c2d [file] [log] [blame]
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "oobe_config/oobe_config.h"
#include <optional>
#include <utility>
#include <base/check.h>
#include <base/check_op.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <brillo/secure_blob.h>
#include <libhwsec/status.h>
#include "libhwsec/frontend/oobe_config/frontend.h"
#include "oobe_config/encryption/openssl_encryption.h"
#include "oobe_config/encryption/pstore_storage.h"
#include "oobe_config/network_exporter.h"
#include "oobe_config/rollback_data.pb.h"
namespace oobe_config {
namespace {
void ResetRollbackSpace(const hwsec::OobeConfigFrontend* hwsec_oobe_config) {
hwsec::Status space_reset = hwsec_oobe_config->ResetRollbackSpace();
if (!space_reset.ok()) {
LOG(ERROR) << "Resetting rollback space failed: " << space_reset.status();
// TODO(b/262235959): Report failure to reset rollback space.
}
}
} // namespace
OobeConfig::OobeConfig(const hwsec::OobeConfigFrontend* hwsec_oobe_config,
FileHandler file_handler)
: hwsec_oobe_config_(hwsec_oobe_config),
file_handler_(std::move(file_handler)) {}
OobeConfig::~OobeConfig() = default;
void OobeConfig::GetRollbackData(RollbackData* rollback_data) const {
if (file_handler_.HasOobeCompletedFlag()) {
// If OOBE has been completed already, we know the EULA has been accepted.
rollback_data->set_eula_auto_accept(true);
}
if (file_handler_.HasMetricsReportingEnabledFlag()) {
rollback_data->set_eula_send_statistics(true);
}
if (network_config_for_testing_.empty()) {
std::optional<std::string> network_config =
oobe_config::ExportNetworkConfig();
if (network_config.has_value()) {
rollback_data->set_network_config(*network_config);
}
} else {
rollback_data->set_network_config(network_config_for_testing_);
}
return;
}
bool OobeConfig::GetSerializedRollbackData(
std::string* serialized_rollback_data) const {
RollbackData rollback_data;
GetRollbackData(&rollback_data);
if (!rollback_data.SerializeToString(serialized_rollback_data)) {
LOG(ERROR) << "Couldn't serialize proto.";
return false;
}
return true;
}
bool OobeConfig::EncryptedRollbackSave(bool run_tpm_encryption) const {
std::string serialized_rollback_data;
if (!GetSerializedRollbackData(&serialized_rollback_data)) {
return false;
}
// TODO(b/263065223): We are in migration stage 1. In production, only encrypt
// data with OpenSSL. `run_tpm_encryption` is true during unit and tast tests.
// This allows us to test TPM-based decryption works.
if (run_tpm_encryption && TpmRollbackSpaceReady()) {
TpmEncryptedRollbackSave(serialized_rollback_data);
}
bool openssl_success = OpenSslEncryptedRollbackSave(serialized_rollback_data);
// While we run both encryptions, we consider success to save
// OpenSSL-encrypted data success overall. If TPM-based encryption fails, we
// can still run rollback successfully.
if (!openssl_success)
return false;
if (!file_handler_.CreateDataSavedFlag()) {
LOG(ERROR) << "Failed to write data saved flag.";
return false;
}
return true;
}
bool OobeConfig::EncryptedRollbackRestore() const {
std::optional<brillo::SecureBlob> decrypted_data;
if (TpmRollbackSpaceReady()) {
decrypted_data = TpmEncryptedRollbackRestore();
}
// Only attempt openssl decryption if tpm-based decryption didn't happen or
// failed.
if (!decrypted_data.has_value()) {
decrypted_data = OpensslEncryptedRollbackRestore();
}
if (!decrypted_data.has_value()) {
return false;
}
std::string rollback_data_str = decrypted_data->to_string();
// Write the unencrypted data to disk.
if (!file_handler_.WriteDecryptedRollbackData(rollback_data_str)) {
return false;
}
return true;
}
bool OobeConfig::TpmRollbackSpaceReady() const {
hwsec::Status space_ready = hwsec_oobe_config_->IsRollbackSpaceReady();
if (!space_ready.ok()) {
if (space_ready->ToTPMRetryAction() ==
hwsec::TPMRetryAction::kSpaceNotFound) {
// TODO(b/262235959): Maybe add a metric here, but make sure it is only
// reported on data restore because data save reporting is unreliable.
// Not finding space is expected, log as informational.
LOG(INFO) << "Rollback space does not exist. Status: "
<< space_ready.status();
} else {
LOG(ERROR) << "Failed to check if rollback space exists. Status: "
<< space_ready.status();
}
return false;
}
LOG(INFO) << "Rollback space found.";
return true;
}
bool OobeConfig::TpmEncryptedRollbackSave(
const std::string& rollback_data) const {
DCHECK(TpmRollbackSpaceReady());
LOG(INFO) << "Attempting encryption using rollback space.";
hwsec::StatusOr<brillo::Blob> encrypted_rollback_data_tpm =
hwsec_oobe_config_->Encrypt(brillo::SecureBlob(rollback_data));
if (!encrypted_rollback_data_tpm.ok()) {
LOG(ERROR) << "Falling back to openssl encryption. Status: "
<< encrypted_rollback_data_tpm.status();
return false;
}
if (!file_handler_.WriteTpmEncryptedRollbackData(
brillo::BlobToString(*encrypted_rollback_data_tpm))) {
LOG(ERROR) << "Failed to write TPM-encrypted rollback data file. "
"Falling back to openssl encryption.";
return false;
}
LOG(INFO) << "Finished TPM-based encryption.";
return true;
}
bool OobeConfig::OpenSslEncryptedRollbackSave(
const std::string& rollback_data) const {
LOG(INFO) << "Attempting encryption using OpenSSL.";
std::optional<EncryptedData> encrypted_rollback_data =
Encrypt(brillo::SecureBlob(rollback_data));
if (!encrypted_rollback_data) {
LOG(ERROR) << "Failed to encrypt with openssl.";
return false;
}
if (!StageForPstore(encrypted_rollback_data->key.to_string(),
file_handler_)) {
LOG(ERROR)
<< "Failed to prepare data for storage in the encrypted reboot vault";
return false;
}
if (!file_handler_.WriteOpensslEncryptedRollbackData(
brillo::BlobToString(encrypted_rollback_data->data))) {
LOG(ERROR) << "Failed to write openssl-encrypted rollback data file.";
return false;
}
LOG(INFO) << "Successfully encrypted data with OpenSSL.";
return true;
}
std::optional<brillo::SecureBlob> OobeConfig::TpmEncryptedRollbackRestore()
const {
DCHECK(TpmRollbackSpaceReady());
LOG(INFO) << "Attempting decryption using rollback space.";
std::string encrypted_data_tpm;
if (!file_handler_.ReadTpmEncryptedRollbackData(&encrypted_data_tpm)) {
LOG(WARNING)
<< "TPM has rollback space but found no TPM-encrypted rollback data "
"file. This is expected if you did not request to encrypt with TPM.";
// TODO(b/262235959): Report failure to find TPM-encrypted rollback data.
ResetRollbackSpace(hwsec_oobe_config_);
return std::nullopt;
}
hwsec::StatusOr<brillo::SecureBlob> decrypted_data_tpm =
hwsec_oobe_config_->Decrypt(brillo::BlobFromString(encrypted_data_tpm));
if (!decrypted_data_tpm.ok()) {
LOG(WARNING) << "TPM decryption failed. This may happen in rare cases, "
"e.g. when TPM is cleared during rollback OOBE. Falling "
"back to OpenSSL decryption. Status: "
<< decrypted_data_tpm.status();
// TODO(b/262235959): Report failure to decrypt data with TPM.
return std::nullopt;
}
LOG(INFO) << "Successfully decrypted TPM-encrypted rollback data.";
// TODO(b/262235959): Report TPM-based encryption finished successfully.
ResetRollbackSpace(hwsec_oobe_config_);
return decrypted_data_tpm.value();
}
std::optional<brillo::SecureBlob> OobeConfig::OpensslEncryptedRollbackRestore()
const {
LOG(INFO) << "Attempting decryption using OpenSSL.";
std::optional<std::string> key = LoadFromPstore(file_handler_);
if (!key.has_value()) {
LOG(ERROR) << "Failed to load key from pstore.";
return std::nullopt;
}
std::string encrypted_data;
if (!file_handler_.ReadOpensslEncryptedRollbackData(&encrypted_data)) {
return std::nullopt;
}
std::optional<brillo::SecureBlob> decrypted_data = Decrypt(
{brillo::BlobFromString(encrypted_data), brillo::SecureBlob(*key)});
if (!decrypted_data.has_value()) {
LOG(ERROR) << "Could not decrypt OpenSSL-encrypted rollback data.";
return std::nullopt;
}
LOG(INFO) << "Successfully decrypted data with OpenSSL.";
return decrypted_data;
}
} // namespace oobe_config