普通文本  |  354行  |  12.5 KB

//
// 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