blob: 66a1a2953d162df2dcb9174a114f9d878f8e9100 [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 "diagnostics/cros_healthd/fetchers/tpm_fetcher.h"
#include <string>
#include <utility>
#include <attestation-client/attestation/dbus-proxies.h>
#include <base/callback.h>
#include <base/check.h>
#include <base/time/time.h>
#include <brillo/errors/error.h>
#include <dbus/object_proxy.h>
#include <tpm_manager-client/tpm_manager/dbus-proxies.h>
#include "diagnostics/common/dbus_utils.h"
#include "diagnostics/cros_healthd/utils/error_utils.h"
#include "diagnostics/cros_healthd/utils/file_utils.h"
namespace diagnostics {
namespace {
namespace mojo_ipc = ::chromeos::cros_healthd::mojom;
// Tpm manager and attestation require a long timeout.
const int64_t DBUS_TIMEOUT_MS =
base::TimeDelta::FromMinutes(2).InMilliseconds();
mojo_ipc::TpmGSCVersion GetGscVersion(
const tpm_manager::GetVersionInfoReply& reply) {
switch (reply.gsc_version()) {
case tpm_manager::GSC_VERSION_NOT_GSC:
return mojo_ipc::TpmGSCVersion::kNotGSC;
case tpm_manager::GSC_VERSION_CR50:
return mojo_ipc::TpmGSCVersion::kCr50;
case tpm_manager::GSC_VERSION_TI50:
return mojo_ipc::TpmGSCVersion::kTi50;
}
}
} // namespace
void TpmFetcher::FetchVersion() {
tpm_manager::GetVersionInfoRequest request;
auto [on_success, on_error] = SplitDbusCallback(
base::BindOnce(&TpmFetcher::HandleVersion, weak_factory_.GetWeakPtr()));
context_->tpm_manager_proxy()->GetVersionInfoAsync(
request, std::move(on_success), std::move(on_error), DBUS_TIMEOUT_MS);
}
void TpmFetcher::HandleVersion(brillo::Error* err,
const tpm_manager::GetVersionInfoReply& reply) {
DCHECK(info_);
if (err) {
SendError("Failed to call TpmManager::GetVersionInfo(): " +
err->GetMessage());
return;
}
if (reply.status() != tpm_manager::STATUS_SUCCESS) {
SendError("TpmManager::GetVersionInfo() returned error status: " +
std::to_string(reply.status()));
return;
}
auto version = mojo_ipc::TpmVersion::New();
version->gsc_version = GetGscVersion(reply);
version->family = reply.family();
version->spec_level = reply.spec_level();
version->manufacturer = reply.manufacturer();
version->tpm_model = reply.tpm_model();
version->firmware_version = reply.firmware_version();
version->vendor_specific = reply.vendor_specific().empty()
? base::nullopt
: base::make_optional(reply.vendor_specific());
info_->version = std::move(version);
CheckAndSendInfo();
}
void TpmFetcher::FetchStatus() {
tpm_manager::GetTpmNonsensitiveStatusRequest request;
auto [on_success, on_error] = SplitDbusCallback(
base::BindOnce(&TpmFetcher::HandleStatus, weak_factory_.GetWeakPtr()));
context_->tpm_manager_proxy()->GetTpmNonsensitiveStatusAsync(
request, std::move(on_success), std::move(on_error), DBUS_TIMEOUT_MS);
}
void TpmFetcher::HandleStatus(
brillo::Error* err,
const tpm_manager::GetTpmNonsensitiveStatusReply& reply) {
DCHECK(info_);
if (err) {
SendError("Failed to call TpmManager::GetTpmNonsensitiveStatus(): " +
err->GetMessage());
return;
}
if (reply.status() != tpm_manager::STATUS_SUCCESS) {
SendError("TpmManager::GetTpmNonsensitiveStatus() returned error status: " +
std::to_string(reply.status()));
return;
}
auto status = mojo_ipc::TpmStatus::New();
status->enabled = reply.is_enabled();
status->owned = reply.is_owned();
status->owner_password_is_present = reply.is_owner_password_present();
info_->status = std::move(status);
CheckAndSendInfo();
}
void TpmFetcher::FetchDictionaryAttack() {
tpm_manager::GetDictionaryAttackInfoRequest request;
auto [on_success, on_error] = SplitDbusCallback(base::BindOnce(
&TpmFetcher::HandleDictionaryAttack, weak_factory_.GetWeakPtr()));
context_->tpm_manager_proxy()->GetDictionaryAttackInfoAsync(
request, std::move(on_success), std::move(on_error), DBUS_TIMEOUT_MS);
}
void TpmFetcher::HandleDictionaryAttack(
brillo::Error* err,
const tpm_manager::GetDictionaryAttackInfoReply& reply) {
DCHECK(info_);
if (err) {
SendError("Failed to call TpmManager::GetDictionaryAttackInfo(): " +
err->GetMessage());
return;
}
if (reply.status() != tpm_manager::STATUS_SUCCESS) {
SendError("TpmManager::GetDictionaryAttackInfo() returned error status: " +
std::to_string(reply.status()));
return;
}
auto da = mojo_ipc::TpmDictionaryAttack::New();
da->counter = reply.dictionary_attack_counter();
da->threshold = reply.dictionary_attack_threshold();
da->lockout_in_effect = reply.dictionary_attack_lockout_in_effect();
da->lockout_seconds_remaining =
reply.dictionary_attack_lockout_seconds_remaining();
info_->dictionary_attack = std::move(da);
CheckAndSendInfo();
}
void TpmFetcher::FetchAttestation() {
attestation::GetStatusRequest request;
auto [on_success, on_error] = SplitDbusCallback(base::BindOnce(
&TpmFetcher::HandleAttestation, weak_factory_.GetWeakPtr()));
context_->attestation_proxy()->GetStatusAsync(
request, std::move(on_success), std::move(on_error), DBUS_TIMEOUT_MS);
}
void TpmFetcher::HandleAttestation(brillo::Error* err,
const attestation::GetStatusReply& reply) {
DCHECK(info_);
if (err) {
SendError("Failed to call Attestation::GetStatus(): " + err->GetMessage());
return;
}
if (reply.status() != attestation::STATUS_SUCCESS) {
SendError("TpmManager::GetDictionaryAttackInfo() returned error status: " +
std::to_string(reply.status()));
return;
}
auto data = mojo_ipc::TpmAttestation::New();
data->prepared_for_enrollment = reply.prepared_for_enrollment();
data->enrolled = reply.enrolled();
info_->attestation = std::move(data);
CheckAndSendInfo();
}
void TpmFetcher::FetchSupportedFeatures() {
tpm_manager::GetSupportedFeaturesRequest request;
auto [on_success, on_error] = SplitDbusCallback(base::BindOnce(
&TpmFetcher::HandleSupportedFeatures, weak_factory_.GetWeakPtr()));
context_->tpm_manager_proxy()->GetSupportedFeaturesAsync(
request, std::move(on_success), std::move(on_error), DBUS_TIMEOUT_MS);
}
void TpmFetcher::HandleSupportedFeatures(
brillo::Error* err, const tpm_manager::GetSupportedFeaturesReply& reply) {
DCHECK(info_);
if (err) {
SendError("Failed to call TpmManager::GetSupportedFeatures(): " +
err->GetMessage());
return;
}
if (reply.status() != tpm_manager::STATUS_SUCCESS) {
SendError("TpmManager::GetSupportedFeatures() returned error status: " +
std::to_string(reply.status()));
return;
}
auto data = mojo_ipc::TpmSupportedFeatures::New();
data->support_u2f = reply.support_u2f();
data->support_pinweaver = reply.support_pinweaver();
data->support_runtime_selection = reply.support_runtime_selection();
data->is_allowed = reply.is_allowed();
info_->supported_features = std::move(data);
CheckAndSendInfo();
}
void TpmFetcher::CheckAndSendInfo() {
DCHECK(info_);
if (!info_->version || !info_->status || !info_->dictionary_attack ||
!info_->attestation || !info_->supported_features) {
return;
}
SendResult(mojo_ipc::TpmResult::NewTpmInfo(std::move(info_)));
}
void TpmFetcher::SendError(const std::string& message) {
SendResult(mojo_ipc::TpmResult::NewError(CreateAndLogProbeError(
mojo_ipc::ErrorType::kServiceUnavailable, message)));
}
void TpmFetcher::SendResult(
chromeos::cros_healthd::mojom::TpmResultPtr result) {
// Invalid all weak ptrs to prevent other callbacks to be run.
weak_factory_.InvalidateWeakPtrs();
if (pending_callbacks_.empty())
return;
for (size_t i = 1; i < pending_callbacks_.size(); ++i) {
std::move(pending_callbacks_[i]).Run(result.Clone());
}
std::move(pending_callbacks_[0]).Run(std::move(result));
pending_callbacks_.clear();
}
void TpmFetcher::FetchTpmInfo(TpmFetcher::FetchTpmInfoCallback&& callback) {
pending_callbacks_.push_back(std::move(callback));
// Returns if there is already a pending callback. The second callback will be
// fulfilled when the first one is fulfilled.
if (pending_callbacks_.size() > 1)
return;
info_ = mojo_ipc::TpmInfo::New();
FetchVersion();
FetchStatus();
FetchDictionaryAttack();
FetchAttestation();
FetchSupportedFeatures();
ReadAndTrimString(context_->root_dir().Append(kFileTpmDidVid),
&info_->did_vid);
}
} // namespace diagnostics