blob: 24af86ee83bf5b1aeb929a94526db5473d4d08c8 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "vtpm/commands/get_capability_command.h"
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include <base/functional/callback.h>
#include <base/logging.h>
#include <trunks/command_parser.h>
#include <trunks/error_codes.h>
#include <trunks/response_serializer.h>
#include <trunks/tpm_generated.h>
#include "vtpm/backends/tpm_handle_manager.h"
namespace vtpm {
namespace {
std::string CapabilityToString(trunks::TPM_CAP cap) {
switch (cap) {
case trunks::TPM_CAP_ALGS:
return "TPM_CAP_ALGS";
case trunks::TPM_CAP_HANDLES:
return "TPM_CAP_HANDLES";
case trunks::TPM_CAP_COMMANDS:
return "TPM_CAP_COMMANDS";
case trunks::TPM_CAP_PP_COMMANDS:
return "TPM_CAP_PP_COMMANDS";
case trunks::TPM_CAP_AUDIT_COMMANDS:
return "TPM_CAP_AUDIT_COMMANDS";
case trunks::TPM_CAP_PCRS:
return "TPM_CAP_PCRS";
case trunks::TPM_CAP_TPM_PROPERTIES:
return "TPM_CAP_TPM_PROPERTIES";
case trunks::TPM_CAP_PCR_PROPERTIES:
return "TPM_CAP_PCR_PROPERTIES";
case trunks::TPM_CAP_ECC_CURVES:
return "TPM_CAP_ECC_CURVES";
case trunks::TPM_CAP_VENDOR_PROPERTY:
return "TPM_CAP_VENDOR_PROPERTY";
default:
LOG(DFATAL) << __func__ << " only supports defined TPM-CAP; got: " << cap;
return "(Unknown TPM_CAP)";
}
}
} // namespace
GetCapabilityCommand::GetCapabilityCommand(
trunks::CommandParser* command_parser,
trunks::ResponseSerializer* response_serializer,
Command* direct_forwarder,
TpmHandleManager* tpm_handle_manager,
TpmPropertyManager* tpm_property_manager)
: command_parser_(command_parser),
response_serializer_(response_serializer),
direct_forwarder_(direct_forwarder),
tpm_handle_manager_(tpm_handle_manager),
tpm_property_manager_(tpm_property_manager) {
CHECK(command_parser_);
CHECK(response_serializer_);
CHECK(direct_forwarder_);
CHECK(tpm_handle_manager_);
CHECK(tpm_property_manager_);
}
void GetCapabilityCommand::Run(const std::string& command,
CommandResponseCallback callback) {
trunks::TPM_CAP cap;
trunks::UINT32 property;
trunks::UINT32 property_count;
std::string buffer = command;
trunks::TPM_RC rc = command_parser_->ParseCommandGetCapability(
&buffer, &cap, &property, &property_count);
if (rc) {
ReturnWithError(rc, std::move(callback));
return;
}
// If the capability is not defined by TPM2.0 spec, return error.
if ((cap < trunks::TPM_CAP_FIRST || cap > trunks::TPM_CAP_LAST) &&
cap != trunks::TPM_CAP_VENDOR_PROPERTY) {
LOG(ERROR) << __func__ << ": Unexpected capability: " << cap;
ReturnWithError(trunks::TPM_RC_VALUE, std::move(callback));
return;
}
trunks::TPMI_YES_NO has_more = NO;
trunks::TPMS_CAPABILITY_DATA cap_data = {.capability = cap};
switch (cap) {
// The implementation of the algorithms are backed by the host TPM.
case trunks::TPM_CAP_ALGS:
return direct_forwarder_->Run(command, std::move(callback));
case trunks::TPM_CAP_HANDLES:
rc = GetCapabilityTpmHandles(property, property_count, has_more,
cap_data.data.handles);
break;
case trunks::TPM_CAP_COMMANDS:
rc = GetCapabilityCommands(property, property_count, has_more,
cap_data.data.command);
break;
case trunks::TPM_CAP_PCRS:
rc = GetCapabilityPCRs(property, property_count, has_more,
cap_data.data.assigned_pcr);
break;
case trunks::TPM_CAP_TPM_PROPERTIES:
rc = GetCapabilityTpmProperties(property, property_count, has_more,
cap_data.data.tpm_properties);
break;
default:
LOG(ERROR) << __func__
<< ": Unimplemented capability: " << CapabilityToString(cap);
rc = trunks::TPM_RC_VALUE;
break;
}
if (rc) {
ReturnWithError(rc, std::move(callback));
return;
}
std::string response;
response_serializer_->SerializeResponseGetCapability(has_more, cap_data,
&response);
std::move(callback).Run(response);
return;
}
trunks::TPM_RC GetCapabilityCommand::GetCapabilityTpmHandles(
trunks::UINT32 property,
trunks::UINT32 property_count,
trunks::TPMI_YES_NO& has_more,
trunks::TPML_HANDLE& handles) {
if (!tpm_handle_manager_->IsHandleTypeSuppoerted(property)) {
// Return empty handles if the handle type is not supporeted.
has_more = NO;
handles.count = 0;
return trunks::TPM_RC_SUCCESS;
}
std::vector<trunks::TPM_HANDLE> found_handles;
trunks::TPM_RC rc =
tpm_handle_manager_->GetHandleList(property, &found_handles);
if (rc) {
LOG(ERROR) << __func__
<< ": Failed to get handle list: " << trunks::GetErrorString(rc);
return rc;
}
if (property_count > MAX_CAP_HANDLES) {
property_count = MAX_CAP_HANDLES;
}
has_more = (found_handles.size() > property_count ? YES : NO);
handles.count = has_more ? property_count : found_handles.size();
std::copy(found_handles.begin(), found_handles.begin() + handles.count,
handles.handle);
return trunks::TPM_RC_SUCCESS;
}
trunks::TPM_RC GetCapabilityCommand::GetCapabilityCommands(
trunks::UINT32 property,
trunks::UINT32 property_count,
trunks::TPMI_YES_NO& has_more,
trunks::TPML_CCA& commands) {
const std::vector<trunks::TPM_CC>& command_list =
tpm_property_manager_->GetCommandList();
// Get the commands from the lower bound.
auto iter =
std::lower_bound(command_list.cbegin(), command_list.cend(), property);
const size_t command_count = std::distance(iter, command_list.cend());
commands.count = std::min({static_cast<size_t>(property_count), command_count,
std::size(commands.command_attributes)});
has_more = (commands.count < command_count ? YES : NO);
for (int i = 0; i < commands.count; ++i, ++iter) {
commands.command_attributes[i] = *iter;
}
return trunks::TPM_RC_SUCCESS;
}
trunks::TPM_RC GetCapabilityCommand::GetCapabilityPCRs(
trunks::UINT32 property,
trunks::UINT32 property_count,
trunks::TPMI_YES_NO& has_more,
trunks::TPML_PCR_SELECTION& assigned_pcr) {
// we don't support PCR; just set the everything to 0.
assigned_pcr.count = 0;
return trunks::TPM_RC_SUCCESS;
}
trunks::TPM_RC GetCapabilityCommand::GetCapabilityTpmProperties(
trunks::UINT32 property,
trunks::UINT32 property_count,
trunks::TPMI_YES_NO& has_more,
trunks::TPML_TAGGED_TPM_PROPERTY& tpm_properties) {
const std::vector<trunks::TPMS_TAGGED_PROPERTY>& capability_properties_list =
tpm_property_manager_->GetCapabilityPropertyList();
// The spec asks us to provide the first property on or after the provided
// "property" handle.
auto iter =
std::lower_bound(capability_properties_list.cbegin(),
capability_properties_list.cend(), property,
[](const trunks::TPMS_TAGGED_PROPERTY& tagged_prop,
const trunks::UINT32& property) {
return tagged_prop.property < property;
});
const size_t capability_properties_count =
std::distance(iter, capability_properties_list.cend());
tpm_properties.count = std::min({static_cast<size_t>(property_count),
capability_properties_count,
std::size(tpm_properties.tpm_property)});
has_more = (tpm_properties.count < capability_properties_count ? YES : NO);
for (int i = 0; i < tpm_properties.count; i++, ++iter) {
tpm_properties.tpm_property[i] = *iter;
}
return trunks::TPM_RC_SUCCESS;
}
void GetCapabilityCommand::ReturnWithError(trunks::TPM_RC rc,
CommandResponseCallback callback) {
DCHECK_NE(rc, trunks::TPM_RC_SUCCESS);
std::string response;
response_serializer_->SerializeHeaderOnlyResponse(rc, &response);
std::move(callback).Run(response);
}
} // namespace vtpm