blob: a186d6be39a0d4586e9eddbc5be59b5b385279dd [file] [log] [blame]
//
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "tpm_manager/server/tpm_initializer_impl.h"
#include <string>
#include <base/logging.h>
#include <base/stl_util.h>
#include <base/strings/string_number_conversions.h>
#include <trousers/scoped_tss_type.h>
#include "tpm_manager/common/tpm_manager_constants.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"
namespace {
const int kMaxOwnershipTimeoutRetries = 5;
const char* kWellKnownSrkSecret = "well_known_srk_secret";
} // namespace
namespace tpm_manager {
TpmInitializerImpl::TpmInitializerImpl(
LocalDataStore* local_data_store,
TpmStatus* tpm_status,
const OwnershipTakenCallBack& ownership_taken_callback)
: local_data_store_(local_data_store),
tpm_status_(tpm_status),
ownership_taken_callback_(ownership_taken_callback) {}
bool TpmInitializerImpl::PreInitializeTpm() {
// No pre-initialization steps are performed for 1.2.
return true;
}
bool TpmInitializerImpl::InitializeTpm() {
if (TpmStatus::kTpmOwned == tpm_status_->CheckAndNotifyIfTpmOwned()) {
// Tpm is already owned, so we do not need to do anything.
VLOG(1) << "Tpm already owned.";
return true;
}
TpmConnection connection(GetDefaultOwnerPassword());
if (!InitializeEndorsementKey(&connection) || !TakeOwnership(&connection) ||
!InitializeSrk(&connection)) {
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.";
return false;
}
if (!ChangeOwnerPassword(&connection, owner_password)) {
return false;
}
tpm_status_->MarkOwnerPasswordStateDirty();
if (!ownership_taken_callback_.is_null()) {
ownership_taken_callback_.Run();
}
return true;
}
void TpmInitializerImpl::VerifiedBootHelper() {
// Nothing to do.
}
bool TpmInitializerImpl::ResetDictionaryAttackLock() {
LOG(WARNING) << __func__ << ": Not implemented.";
return false;
}
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;
}
// At this point the EK does not exist, so we create 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) {
if (tpm_status_->TestTpmWithDefaultOwnerPassword()) {
VLOG(1) << "The Tpm already has the default owner password.";
return true;
}
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::string_as_array(&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;
}
} // namespace tpm_manager