blob: d9a61a9c2bf87e5515f490886f1b72bed7d1a916 [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_nvram_impl.h"
#include <arpa/inet.h>
#include <string>
#include <base/logging.h>
#include <base/stl_util.h>
#include <trousers/scoped_tss_type.h>
#include "tpm_manager/common/local_data.pb.h"
#include "tpm_manager/server/local_data_store.h"
#include "tpm_manager/server/tpm_util.h"
namespace {
// PCR0 at locality 1 is used to differentiate between developed and normal
// mode. Restricting nvram to the PCR0 value in locality 1 prevents nvram from
// persisting across mode switch.
const unsigned int kTpmBootPCR = 0;
const unsigned int kTpmPCRLocality = 1;
} // namespace
namespace tpm_manager {
using trousers::ScopedTssMemory;
using trousers::ScopedTssNvStore;
using trousers::ScopedTssPcrs;
TpmNvramImpl::TpmNvramImpl(LocalDataStore* local_data_store)
: local_data_store_(local_data_store) {}
bool TpmNvramImpl::DefineNvram(uint32_t index, size_t length) {
ScopedTssNvStore nv_handle(tpm_connection_.GetContext());
if (!(InitializeNvramHandle(&nv_handle, index) &&
SetOwnerPolicy(&nv_handle))) {
return false;
}
TSS_RESULT result;
result =
Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_DATASIZE, 0, length);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set size on NVRAM object: " << length;
return false;
}
// Restrict to only one write.
result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_PERMISSIONS, 0,
TPM_NV_PER_WRITEDEFINE);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set PER_WRITEDEFINE on NVRAM object";
return false;
}
// Restrict to writing only with owner authorization.
result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_PERMISSIONS, 0,
TPM_NV_PER_OWNERWRITE);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set PER_OWNERWRITE on NVRAM object";
return false;
}
ScopedTssPcrs pcr_handle(tpm_connection_.GetContext());
if (!SetCompositePcr0(&pcr_handle)) {
return false;
}
result = Tspi_NV_DefineSpace(nv_handle,
pcr_handle /* ReadPCRs restricted to PCR0 */,
pcr_handle /* WritePCRs restricted to PCR0 */);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not define NVRAM space: " << index;
return false;
}
return true;
}
bool TpmNvramImpl::DestroyNvram(uint32_t index) {
bool defined;
if (!IsNvramDefined(index, &defined)) {
return false;
}
if (!defined) {
// If the nvram space is not defined, we don't need to destroy it.
return true;
}
ScopedTssNvStore nv_handle(tpm_connection_.GetContext());
if (!(InitializeNvramHandle(&nv_handle, index) &&
SetOwnerPolicy(&nv_handle))) {
return false;
}
TSS_RESULT result = Tspi_NV_ReleaseSpace(nv_handle);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not release NVRAM space: " << index;
return false;
}
return true;
}
bool TpmNvramImpl::WriteNvram(uint32_t index, const std::string& data) {
ScopedTssNvStore nv_handle(tpm_connection_.GetContext());
if (!(InitializeNvramHandle(&nv_handle, index) &&
SetOwnerPolicy(&nv_handle))) {
return false;
}
TSS_RESULT result = Tspi_NV_WriteValue(
nv_handle, 0 /* offset */, data.size(),
reinterpret_cast<BYTE*>(const_cast<char*>(data.data())));
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not write to NVRAM space: " << index;
return false;
}
return true;
}
bool TpmNvramImpl::ReadNvram(uint32_t index, std::string* data) {
CHECK(data);
TSS_RESULT result;
ScopedTssNvStore nv_handle(tpm_connection_.GetContext());
if (!InitializeNvramHandle(&nv_handle, index)) {
return false;
}
size_t nvram_size;
if (!GetNvramSize(index, &nvram_size)) {
return false;
}
data->resize(nvram_size);
// The Tpm1.2 Specification defines the maximum read size of 128 bytes.
// Therefore we have to loop through the data returned.
const size_t kMaxDataSize = 128;
uint32_t offset = 0;
while (offset < nvram_size) {
uint32_t chunk_size = std::max(nvram_size - offset, kMaxDataSize);
ScopedTssMemory space_data(tpm_connection_.GetContext());
if ((result = Tspi_NV_ReadValue(nv_handle, offset, &chunk_size,
space_data.ptr()))) {
TPM_LOG(ERROR, result) << "Could not read from NVRAM space: " << index;
return false;
}
if (!space_data.value()) {
LOG(ERROR) << "No data read from NVRAM space: " << index;
return false;
}
CHECK_LE((offset + chunk_size), data->size());
data->replace(offset, chunk_size,
reinterpret_cast<char*>(space_data.value()), chunk_size);
offset += chunk_size;
}
return true;
}
bool TpmNvramImpl::IsNvramDefined(uint32_t index, bool* defined) {
CHECK(defined);
uint32_t nv_list_data_length = 0;
ScopedTssMemory nv_list_data(tpm_connection_.GetContext());
TSS_RESULT result =
Tspi_TPM_GetCapability(tpm_connection_.GetTpm(), TSS_TPMCAP_NV_LIST, 0,
NULL, &nv_list_data_length, nv_list_data.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetCapability";
return false;
}
// Walk the list and check if the index exists.
uint32_t* nv_list = reinterpret_cast<uint32_t*>(nv_list_data.value());
uint32_t nv_list_length = nv_list_data_length / sizeof(uint32_t);
index = htonl(index); // TPM data is network byte order.
for (uint32_t i = 0; i < nv_list_length; ++i) {
if (index == nv_list[i]) {
*defined = true;
return true;
}
}
*defined = false;
return true;
}
bool TpmNvramImpl::IsNvramLocked(uint32_t index, bool* locked) {
CHECK(locked);
uint32_t nv_index_data_length = 0;
ScopedTssMemory nv_index_data(tpm_connection_.GetContext());
TSS_RESULT result =
Tspi_TPM_GetCapability(tpm_connection_.GetTpm(), TSS_TPMCAP_NV_INDEX,
sizeof(index), reinterpret_cast<BYTE*>(&index),
&nv_index_data_length, nv_index_data.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetCapability";
return false;
}
if (nv_index_data_length < (sizeof(uint32_t) + sizeof(TPM_BOOL))) {
return false;
}
// TPM_NV_DATA_PUBLIC->bWriteDefine is the second to last element in the
// struct.
uint32_t* nv_data_public =
reinterpret_cast<uint32_t*>(nv_index_data.value() + nv_index_data_length -
(sizeof(uint32_t) + sizeof(TPM_BOOL)));
*locked = (*nv_data_public != 0);
return true;
}
bool TpmNvramImpl::GetNvramSize(uint32_t index, size_t* size) {
CHECK(size);
UINT32 nv_index_data_length = 0;
ScopedTssMemory nv_index_data(tpm_connection_.GetContext());
TSS_RESULT result =
Tspi_TPM_GetCapability(tpm_connection_.GetTpm(), TSS_TPMCAP_NV_INDEX,
sizeof(index), reinterpret_cast<BYTE*>(&index),
&nv_index_data_length, nv_index_data.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetCapability";
return false;
}
if (nv_index_data_length < sizeof(uint32_t)) {
return false;
}
// TPM_NV_DATA_PUBLIC->dataSize is the last element in the struct.
uint32_t* nv_data_public = reinterpret_cast<uint32_t*>(
nv_index_data.value() + nv_index_data_length - sizeof(uint32_t));
*size = htonl(*nv_data_public);
return true;
}
bool TpmNvramImpl::InitializeNvramHandle(ScopedTssNvStore* nv_handle,
uint32_t index) {
TSS_RESULT result = Tspi_Context_CreateObject(
tpm_connection_.GetContext(), TSS_OBJECT_TYPE_NV, 0, nv_handle->ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not acquire an NVRAM object handle";
return false;
}
result = Tspi_SetAttribUint32(nv_handle->value(), TSS_TSPATTRIB_NV_INDEX, 0,
index);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set index on NVRAM object: " << index;
return false;
}
return true;
}
bool TpmNvramImpl::SetOwnerPolicy(ScopedTssNvStore* nv_handle) {
trousers::ScopedTssPolicy policy_handle(tpm_connection_.GetContext());
TSS_RESULT result;
result = Tspi_Context_CreateObject(tpm_connection_.GetContext(),
TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE,
policy_handle.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return false;
}
std::string owner_password;
if (!GetOwnerPassword(&owner_password)) {
return false;
}
result = Tspi_Policy_SetSecret(
policy_handle, TSS_SECRET_MODE_PLAIN, owner_password.size(),
reinterpret_cast<BYTE*>(const_cast<char*>(owner_password.data())));
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
return false;
}
result =
Tspi_Policy_AssignToObject(policy_handle.value(), nv_handle->value());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set NVRAM object policy.";
return false;
}
return true;
}
bool TpmNvramImpl::SetCompositePcr0(ScopedTssPcrs* pcr_handle) {
TSS_RESULT result = Tspi_Context_CreateObject(
tpm_connection_.GetContext(), TSS_OBJECT_TYPE_PCRS,
TSS_PCRS_STRUCT_INFO_SHORT, pcr_handle->ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not acquire PCR object handle";
return false;
}
uint32_t pcr_len;
std::string owner_password;
if (!GetOwnerPassword(&owner_password)) {
return false;
}
ScopedTssMemory pcr_value(tpm_connection_.GetContext());
result = Tspi_TPM_PcrRead(tpm_connection_.GetTpmWithAuth(owner_password),
kTpmBootPCR, &pcr_len, pcr_value.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not read PCR0 value";
return false;
}
result = Tspi_PcrComposite_SetPcrValue(pcr_handle->value(), kTpmBootPCR,
pcr_len, pcr_value.value());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set value for PCR0 in PCR handle";
return false;
}
result =
Tspi_PcrComposite_SetPcrLocality(pcr_handle->value(), kTpmPCRLocality);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set locality for PCR0 in PCR handle";
return false;
}
return true;
}
bool TpmNvramImpl::GetOwnerPassword(std::string* owner_password) {
LocalData local_data;
if (!local_data_store_->Read(&local_data)) {
LOG(ERROR) << "Error reading local data for owner password.";
return false;
}
if (local_data.owner_password().empty()) {
LOG(ERROR) << "No owner password present in tpm local_data.";
return false;
}
owner_password->assign(local_data.owner_password());
return true;
}
} // namespace tpm_manager