blob: 7dc8f5d1763e814984327c49c637ed7f6c288552 [file] [log] [blame]
// Copyright 2015 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 "tpm_manager/server/tpm_initializer_impl.h"
#include <memory>
#include <string>
#include <vector>
#include <base/check.h>
#include <base/logging.h>
#include <base/stl_util.h>
#include <base/strings/string_number_conversions.h>
#include <libhwsec/overalls/overalls_api.h>
#include <tpm_manager-client/tpm_manager/dbus-constants.h>
#include <trousers/scoped_tss_type.h>
#include <trousers/trousers.h>
#include <trousers/tss.h>
#include "tpm_manager/server/local_data_store.h"
#include "tpm_manager/server/tpm_connection.h"
#include "tpm_manager/server/tpm_status.h"
#include "tpm_manager/server/tpm_util.h"
using ::hwsec::overalls::GetOveralls;
namespace {
constexpr int kMaxOwnershipTimeoutRetries = 5;
constexpr char kWellKnownSrkSecret[] = "well_known_srk_secret";
constexpr int kDelegateSecretSize = 20;
constexpr uint8_t kDefaultDelegateLabel = 2;
constexpr uint8_t kDefaultDelegateFamilyLabel = 1;
} // namespace
namespace tpm_manager {
TpmInitializerImpl::TpmInitializerImpl(LocalDataStore* local_data_store,
TpmStatus* tpm_status)
: local_data_store_(local_data_store), tpm_status_(tpm_status) {}
bool TpmInitializerImpl::PreInitializeTpm() {
// No pre-initialization steps are performed for 1.2.
return true;
}
bool TpmInitializerImpl::InitializeTpm() {
TpmStatus::TpmOwnershipStatus ownership_status;
if (!tpm_status_->GetTpmOwned(&ownership_status)) {
LOG(ERROR) << __func__ << ": failed to get tpm ownership status";
return false;
}
if (ownership_status == TpmStatus::kTpmOwned) {
// Tpm is already owned, so we do not need to do anything.
VLOG(1) << "Tpm already owned.";
return true;
}
// Makes sure EK is there when unowned.
if (ownership_status != TpmStatus::kTpmUnowned) {
LOG(INFO) << __func__
<< ": TPM ownership is taken already; skip initializing EK.";
} else if (!InitializeEndorsementKey()) {
LOG(ERROR) << __func__ << ": failed to initialize endorsement key";
return false;
}
TpmConnection connection(GetDefaultOwnerPassword());
if (ownership_status != TpmStatus::kTpmUnowned) {
LOG(INFO) << __func__
<< ": TPM ownership is taken already; skip taking ownership.";
} else if (!TakeOwnership(&connection)) {
LOG(ERROR) << __func__ << ": failed to take TPM ownership";
return false;
}
// TPM ownership is taken; now the status is pre-owned.
if (!InitializeSrk(&connection)) {
LOG(ERROR) << __func__ << ": failed to initialize SRK";
return false;
}
std::string owner_password;
std::string random_bytes;
if (!openssl_util_.GetRandomBytes(kOwnerPasswordRandomBytes, &random_bytes)) {
return false;
}
owner_password = base::HexEncode(random_bytes.data(), random_bytes.size());
LocalData local_data;
local_data.clear_owner_dependency();
for (auto value : kInitialTpmOwnerDependencies) {
local_data.add_owner_dependency(value);
}
local_data.set_owner_password(owner_password);
if (!local_data_store_->Write(local_data)) {
LOG(ERROR) << ": Error saving local data after |set_owner_password|.";
return false;
}
if (!ChangeOwnerPassword(&connection, owner_password)) {
return false;
}
tpm_status_->MarkRandomOwnerPasswordSet();
// for performance sake, continue using the same |local_data| so we don't need
// to read the data from file once again.
AuthDelegate owner_delegate;
if (CreateDelegateWithDefaultLabel(&owner_delegate)) {
local_data.mutable_owner_delegate()->Swap(&owner_delegate);
if (!local_data_store_->Write(local_data)) {
LOG(ERROR) << ": Cannot persist delegate.";
return false;
}
} else {
LOG(ERROR) << __func__ << ": Cannot create delegate.";
return false;
}
reset_da_lock_auth_failed_ = false;
return true;
}
void TpmInitializerImpl::VerifiedBootHelper() {
// Nothing to do.
}
DictionaryAttackResetStatus TpmInitializerImpl::ResetDictionaryAttackLock() {
if (reset_da_lock_auth_failed_) {
// An auth error was encountered in a previous attempt, and there was no
// auth update after the attempt. Skips the request to avoid further
// increasing the counter.
LOG(ERROR) << __func__
<< ": skipped the request to avoid repeating a "
"previous auth error.";
return DictionaryAttackResetStatus::kResetAttemptFailed;
}
TpmStatus::TpmOwnershipStatus ownership_status;
if (!tpm_status_->GetTpmOwned(&ownership_status)) {
// Can't tell if we really can't get tpm ownership status or lockout is in
// our way, so let's still go ahead.
LOG(WARNING) << __func__
<< ": failed to get tpm ownership status, but that could be "
"caused by a locked out TPM, so proceeding anyway.";
} else {
if (ownership_status != TpmStatus::kTpmOwned) {
LOG(ERROR) << __func__ << ": TPM is not initialized yet.";
return DictionaryAttackResetStatus::kResetAttemptFailed;
}
}
std::string owner_password;
AuthDelegate owner_delegate;
if (!ReadOwnerAuthFromLocalData(&owner_password, &owner_delegate)) {
// Note that if it failed here, it could be because the TPM is not owned,
// but we tried anyway because we can't get the TPM status. See comments
// above on GetTpmOwned().
LOG(ERROR) << __func__ << ": failed to get owner auth.";
return DictionaryAttackResetStatus::kResetAttemptFailed;
}
std::unique_ptr<TpmConnection> connection;
if (!owner_password.empty()) {
connection = std::make_unique<TpmConnection>(owner_password);
} else if (!owner_delegate.blob().empty() &&
!owner_delegate.secret().empty()) {
if (!owner_delegate.has_reset_lock_permissions()) {
return DictionaryAttackResetStatus::kDelegateNotAllowed;
}
connection = std::make_unique<TpmConnection>(owner_delegate);
} else {
LOG(ERROR) << __func__ << ": available owner auth not found.";
return DictionaryAttackResetStatus::kDelegateNotAvailable;
}
TSS_HTPM tpm_handle = connection->GetTpm();
if (!tpm_handle) {
LOG(ERROR) << __func__ << ": Error getting a TPM handle.";
return DictionaryAttackResetStatus::kResetAttemptFailed;
}
TSS_RESULT result = GetOveralls()->Ospi_TPM_SetStatus(
tpm_handle, TSS_TPMSTATUS_RESETLOCK, true /* value will be ignored */);
if (result != TSS_SUCCESS) {
TPM_LOG(ERROR, result) << __func__ << ": failed to reset DA lock.";
if (TPM_ERROR(TPM_E_AUTHFAIL) == result ||
TPM_ERROR(TPM_E_AUTH2FAIL) == result) {
reset_da_lock_auth_failed_ = true;
}
return result == TPM_ERROR(TPM_E_WRONGPCRVAL)
? DictionaryAttackResetStatus::kInvalidPcr0State
: DictionaryAttackResetStatus::kResetAttemptFailed;
}
LOG(INFO) << __func__ << ": dictionary attack counter has been reset.";
return DictionaryAttackResetStatus::kResetAttemptSucceeded;
}
TpmInitializerStatus TpmInitializerImpl::DisableDictionaryAttackMitigation() {
return TpmInitializerStatus::kNotSupport;
}
void TpmInitializerImpl::PruneStoredPasswords() {
TpmStatus::TpmOwnershipStatus ownership_status;
if (!tpm_status_->GetTpmOwned(&ownership_status)) {
LOG(ERROR) << __func__ << ": failed to get tpm ownership status";
return;
}
if (ownership_status == TpmStatus::kTpmOwned) {
LOG(WARNING) << __func__
<< ": TPM is already owned. Local data won't be touched.";
return;
}
LocalData local_data;
if (!local_data_store_->Read(&local_data)) {
LOG(ERROR) << __func__ << ": failed to read local data.";
return;
}
local_data.clear_owner_password();
local_data.clear_owner_delegate();
local_data.clear_owner_dependency();
if (!local_data_store_->Write(local_data)) {
LOG(ERROR) << __func__ << ": failed to write local data.";
}
}
bool TpmInitializerImpl::InitializeEndorsementKey() {
TpmConnection connection;
trousers::ScopedTssKey local_key_handle(connection.GetContext());
TSS_RESULT result = Tspi_TPM_GetPubEndorsementKey(
connection.GetTpm(), false, nullptr, local_key_handle.ptr());
if (TPM_ERROR(result) == TPM_SUCCESS) {
// In this case the EK already exists, so we can return true here.
VLOG(1) << "EK already exists.";
return true;
} else if (TPM_ERROR(result) != TPM_E_NO_ENDORSEMENT) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetPubEndorsementKey";
return false;
}
TPM_LOG(INFO, result) << "No EK is present; creating it.";
TSS_FLAG init_flags = TSS_KEY_TYPE_LEGACY | TSS_KEY_SIZE_2048;
if (TPM_ERROR(result = Tspi_Context_CreateObject(
connection.GetContext(), TSS_OBJECT_TYPE_RSAKEY, init_flags,
local_key_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return false;
}
if (TPM_ERROR(result = Tspi_TPM_CreateEndorsementKey(
connection.GetTpm(), local_key_handle, NULL))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_CreateEndorsementKey";
return false;
}
return true;
}
bool TpmInitializerImpl::TakeOwnership(TpmConnection* connection) {
TSS_RESULT result;
trousers::ScopedTssKey srk_handle(connection->GetContext());
TSS_FLAG init_flags = TSS_KEY_TSP_SRK | TSS_KEY_AUTHORIZATION;
if (TPM_ERROR(result = Tspi_Context_CreateObject(
connection->GetContext(), TSS_OBJECT_TYPE_RSAKEY,
init_flags, srk_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return false;
}
TSS_HPOLICY srk_usage_policy;
if (TPM_ERROR(result = Tspi_GetPolicyObject(srk_handle, TSS_POLICY_USAGE,
&srk_usage_policy))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_GetPolicyObject";
return false;
}
if (TPM_ERROR(result = Tspi_Policy_SetSecret(
srk_usage_policy, TSS_SECRET_MODE_PLAIN,
strlen(kWellKnownSrkSecret),
const_cast<BYTE*>(
reinterpret_cast<const BYTE*>(kWellKnownSrkSecret))))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
return false;
}
// Tspi_TPM_TakeOwnership can potentially take a long time to complete,
// so we retry if there is a timeout in any layer. I chose 5, because the
// longest TakeOwnership call that I have seen took ~2min, and the default
// TSS timeout is 30s. This means that after 5 calls, it is quite likely that
// this call will succeed.
int retry_count = 0;
do {
result = Tspi_TPM_TakeOwnership(connection->GetTpm(), srk_handle, 0);
retry_count++;
} while (((result == TDDL_E_TIMEOUT) ||
(result == (TSS_LAYER_TDDL | TDDL_E_TIMEOUT)) ||
(result == (TSS_LAYER_TDDL | TDDL_E_IOERROR))) &&
(retry_count < kMaxOwnershipTimeoutRetries));
if (result) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_TakeOwnership, attempts: "
<< retry_count;
return false;
}
return true;
}
bool TpmInitializerImpl::InitializeSrk(TpmConnection* connection) {
TSS_RESULT result;
trousers::ScopedTssKey srk_handle(connection->GetContext());
TSS_UUID SRK_UUID = TSS_UUID_SRK;
if (TPM_ERROR(result = Tspi_Context_LoadKeyByUUID(
connection->GetContext(), TSS_PS_TYPE_SYSTEM, SRK_UUID,
srk_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_LoadKeyByUUID";
return false;
}
trousers::ScopedTssPolicy policy_handle(connection->GetContext());
if (TPM_ERROR(result = Tspi_Context_CreateObject(
connection->GetContext(), TSS_OBJECT_TYPE_POLICY,
TSS_POLICY_USAGE, policy_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return false;
}
BYTE new_password[0];
if (TPM_ERROR(result = Tspi_Policy_SetSecret(
policy_handle, TSS_SECRET_MODE_PLAIN, 0, new_password))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
return false;
}
if (TPM_ERROR(result = Tspi_ChangeAuth(srk_handle, connection->GetTpm(),
policy_handle))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_ChangeAuth";
return false;
}
TSS_BOOL is_srk_restricted = false;
if (TPM_ERROR(result = Tspi_TPM_GetStatus(connection->GetTpm(),
TSS_TPMSTATUS_DISABLEPUBSRKREAD,
&is_srk_restricted))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetStatus";
return false;
}
// If the SRK is restricted, we unrestrict it.
if (is_srk_restricted) {
if (TPM_ERROR(result = Tspi_TPM_SetStatus(connection->GetTpm(),
TSS_TPMSTATUS_DISABLEPUBSRKREAD,
false))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_SetStatus";
return false;
}
}
return true;
}
bool TpmInitializerImpl::ChangeOwnerPassword(
TpmConnection* connection, const std::string& owner_password) {
TSS_RESULT result;
trousers::ScopedTssPolicy policy_handle(connection->GetContext());
if (TPM_ERROR(result = Tspi_Context_CreateObject(
connection->GetContext(), TSS_OBJECT_TYPE_POLICY,
TSS_POLICY_USAGE, policy_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return false;
}
std::string mutable_owner_password(owner_password);
if (TPM_ERROR(
result = Tspi_Policy_SetSecret(
policy_handle, TSS_SECRET_MODE_PLAIN, owner_password.size(),
reinterpret_cast<BYTE*>(base::data(mutable_owner_password))))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
return false;
}
if (TPM_ERROR(result =
Tspi_ChangeAuth(connection->GetTpm(), 0, policy_handle))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_ChangeAuth";
return false;
}
return true;
}
bool TpmInitializerImpl::ReadOwnerAuthFromLocalData(
std::string* owner_password, AuthDelegate* owner_delegate) {
LocalData local_data;
if (!local_data_store_->Read(&local_data)) {
LOG(ERROR) << __func__ << ": Failed to read local data.";
return false;
}
if (owner_password) {
*owner_password = local_data.owner_password();
}
if (owner_delegate) {
*owner_delegate = local_data.owner_delegate();
}
return true;
}
bool TpmInitializerImpl::CreateDelegateWithDefaultLabel(
AuthDelegate* delegate) {
std::string delegate_blob;
std::string delegate_secret;
// No PCR value bound to the delegate by default (crbug/990322, b/139099154).
if (!CreateAuthDelegate(/*bound_pcrs=*/{}, kDefaultDelegateFamilyLabel,
kDefaultDelegateLabel, &delegate_blob,
&delegate_secret)) {
LOG(ERROR) << __func__ << ": Failed to create delegate.";
return false;
}
delegate->set_blob(delegate_blob);
delegate->set_secret(delegate_secret);
delegate->set_has_reset_lock_permissions(true);
return true;
}
bool TpmInitializerImpl::EnsurePersistentOwnerDelegate() {
LocalData local_data;
if (!local_data_store_->Read(&local_data)) {
LOG(ERROR) << __func__ << ": Failed to read local data.";
return false;
}
auto owner_delegate = local_data.mutable_owner_delegate();
if (!owner_delegate->blob().empty() && !owner_delegate->secret().empty()) {
return true;
}
LOG(WARNING) << __func__ << ": Owner delegate is missing; re-creating.";
if (!CreateDelegateWithDefaultLabel(owner_delegate)) {
LOG(ERROR) << __func__ << ": Failed to create owner delegate.";
return false;
}
if (!local_data_store_->Write(local_data)) {
LOG(ERROR) << __func__ << ": Failed to write local data change.";
return false;
}
return true;
}
bool TpmInitializerImpl::CreateAuthDelegate(
const std::vector<uint32_t>& bound_pcrs,
uint8_t delegate_family_label,
uint8_t delegate_label,
std::string* delegate_blob,
std::string* delegate_secret) {
CHECK(delegate_blob && delegate_secret);
// Connects to the TPM as the owner.
// read the owner password.
// TODO(cylai): provide a clean way to retrieve owner password for this class.
std::string owner_password;
if (!ReadOwnerAuthFromLocalData(&owner_password, nullptr) ||
owner_password.empty()) {
LOG(ERROR) << __func__ << ": couldn't get owner password.";
return false;
}
TpmConnection connection(owner_password);
TSS_HCONTEXT context_handle = connection.GetContext();
TSS_HTPM tpm_handle = connection.GetTpm();
if (!context_handle || !tpm_handle) {
LOG(ERROR) << __func__ << "TPM connection error.";
return false;
}
// Generate a delegate secret.
if (!openssl_util_.GetRandomBytes(kDelegateSecretSize, delegate_secret)) {
return false;
}
// Create an owner delegation policy.
trousers::ScopedTssPolicy policy(context_handle);
TSS_RESULT result;
result = Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_POLICY,
TSS_POLICY_USAGE, policy.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to create policy.";
return false;
}
result = Tspi_Policy_SetSecret(
policy, TSS_SECRET_MODE_PLAIN, delegate_secret->size(),
reinterpret_cast<BYTE*>(const_cast<char*>(delegate_secret->data())));
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to set policy secret.";
return false;
}
result =
Tspi_SetAttribUint32(policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_TYPE, TSS_DELEGATIONTYPE_OWNER);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to set delegation type.";
return false;
}
// These are the privileged operations we will allow the delegate to perform.
constexpr UINT32 permissions =
TPM_DELEGATE_ActivateIdentity | TPM_DELEGATE_DAA_Join |
TPM_DELEGATE_DAA_Sign | TPM_DELEGATE_ResetLockValue |
TPM_DELEGATE_OwnerReadInternalPub | TPM_DELEGATE_CMK_ApproveMA |
TPM_DELEGATE_CMK_CreateTicket | TPM_DELEGATE_AuthorizeMigrationKey;
result = Tspi_SetAttribUint32(policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_PER1, permissions);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to set permissions.";
return false;
}
result = Tspi_SetAttribUint32(policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_PER2, 0);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to set permissions.";
return false;
}
// Bind the delegate to the specified PCRs. Note: it's crucial to pass a null
// TSS_HPCRS to Tspi_TPM_Delegate_CreateDelegation() when no PCR is selected,
// otherwise it will fail with TPM_E_BAD_PARAM_SIZE.
trousers::ScopedTssPcrs pcrs_handle(context_handle);
if (!bound_pcrs.empty()) {
result = Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_PCRS,
TSS_PCRS_STRUCT_INFO_SHORT,
pcrs_handle.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to create PCRS object.";
return false;
}
for (auto bound_pcr : bound_pcrs) {
UINT32 pcr_len = 0;
trousers::ScopedTssMemory pcr_value(context_handle);
if (TPM_ERROR(result = Tspi_TPM_PcrRead(tpm_handle, bound_pcr, &pcr_len,
pcr_value.ptr()))) {
TPM_LOG(ERROR, result) << "Could not read PCR value";
return false;
}
result = Tspi_PcrComposite_SetPcrValue(pcrs_handle, bound_pcr, pcr_len,
pcr_value.value());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set value for PCR in PCRS handle";
return false;
}
}
constexpr unsigned int kTpmPCRLocality = 1;
result = Tspi_PcrComposite_SetPcrLocality(pcrs_handle, kTpmPCRLocality);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result)
<< "Could not set locality for PCRs in PCRS handle";
return false;
}
}
// Create a delegation family.
trousers::ScopedTssObject<TSS_HDELFAMILY> family(context_handle);
result = Tspi_TPM_Delegate_AddFamily(tpm_handle, delegate_family_label,
family.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to create family.";
return false;
}
// Create the delegation.
result = Tspi_TPM_Delegate_CreateDelegation(tpm_handle, delegate_label, 0,
pcrs_handle, family, policy);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to create delegation.";
return false;
}
// Enable the delegation family.
result = Tspi_SetAttribUint32(family, TSS_TSPATTRIB_DELFAMILY_STATE,
TSS_TSPATTRIB_DELFAMILYSTATE_ENABLED, TRUE);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to enable family.";
return false;
}
// Save the delegation blob for later.
if (!GetDataAttribute(context_handle, policy,
TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_OWNERBLOB, delegate_blob)) {
LOG(ERROR) << "CreateDelegate: Failed to get delegate blob.";
return false;
}
return true;
}
bool TpmInitializerImpl::GetDataAttribute(TSS_HCONTEXT context,
TSS_HOBJECT object,
TSS_FLAG flag,
TSS_FLAG sub_flag,
std::string* data) {
UINT32 length = 0;
trousers::ScopedTssMemory buffer(context);
TSS_RESULT result =
Tspi_GetAttribData(object, flag, sub_flag, &length, buffer.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << "Failed to read object attribute.";
return false;
}
data->assign(buffer.value(), buffer.value() + length);
return true;
}
} // namespace tpm_manager