普通文本  |  549行  |  18.69 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/tpm_manager.pb.h"
#include "tpm_manager/server/local_data_store.h"
#include "tpm_manager/server/tpm_util.h"

namespace tpm_manager {

using trousers::ScopedTssMemory;
using trousers::ScopedTssNvStore;
using trousers::ScopedTssPcrs;

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;

void MapAttributesFromTpm(TPM_NV_PER_ATTRIBUTES tpm_flags,
                          std::vector<NvramSpaceAttribute>* attributes) {
  if (tpm_flags & TPM_NV_PER_WRITEDEFINE)
    attributes->push_back(NVRAM_PERSISTENT_WRITE_LOCK);
  if (tpm_flags & TPM_NV_PER_WRITE_STCLEAR)
    attributes->push_back(NVRAM_BOOT_WRITE_LOCK);
  if (tpm_flags & TPM_NV_PER_READ_STCLEAR)
    attributes->push_back(NVRAM_BOOT_READ_LOCK);
  if (tpm_flags & TPM_NV_PER_AUTHWRITE)
    attributes->push_back(NVRAM_WRITE_AUTHORIZATION);
  if (tpm_flags & TPM_NV_PER_AUTHREAD)
    attributes->push_back(NVRAM_READ_AUTHORIZATION);
  if (tpm_flags & TPM_NV_PER_GLOBALLOCK)
    attributes->push_back(NVRAM_GLOBAL_LOCK);
  if (tpm_flags & TPM_NV_PER_PPWRITE)
    attributes->push_back(NVRAM_PLATFORM_WRITE);
  if (tpm_flags & TPM_NV_PER_OWNERWRITE)
    attributes->push_back(NVRAM_OWNER_WRITE);
  if (tpm_flags & TPM_NV_PER_OWNERREAD)
    attributes->push_back(NVRAM_OWNER_READ);
}

TPM_NV_PER_ATTRIBUTES MapAttributesToTpm(
    const std::vector<NvramSpaceAttribute>& attributes) {
  TPM_NV_PER_ATTRIBUTES tpm_flags = 0;
  for (auto attribute : attributes) {
    switch (attribute) {
      case NVRAM_PERSISTENT_WRITE_LOCK:
        tpm_flags |= TPM_NV_PER_WRITEDEFINE;
        break;
      case NVRAM_BOOT_WRITE_LOCK:
        tpm_flags |= TPM_NV_PER_WRITE_STCLEAR;
        break;
      case NVRAM_BOOT_READ_LOCK:
        tpm_flags |= TPM_NV_PER_READ_STCLEAR;
        break;
      case NVRAM_WRITE_AUTHORIZATION:
        tpm_flags |= TPM_NV_PER_AUTHWRITE;
        break;
      case NVRAM_READ_AUTHORIZATION:
        tpm_flags |= TPM_NV_PER_AUTHREAD;
        break;
      case NVRAM_GLOBAL_LOCK:
        tpm_flags |= TPM_NV_PER_GLOBALLOCK;
        break;
      case NVRAM_PLATFORM_WRITE:
        tpm_flags |= TPM_NV_PER_PPWRITE;
        break;
      case NVRAM_OWNER_WRITE:
        tpm_flags |= TPM_NV_PER_OWNERWRITE;
        break;
      case NVRAM_OWNER_READ:
        tpm_flags |= TPM_NV_PER_OWNERREAD;
        break;
      default:
        break;
    }
  }
  return tpm_flags;
}

NvramResult MapTpmError(TSS_RESULT tpm_error) {
  switch (TPM_ERROR(tpm_error)) {
    case TPM_SUCCESS:
      return NVRAM_RESULT_SUCCESS;
    case TPM_E_BAD_PARAMETER:
    case TPM_E_PER_NOWRITE:
    case TPM_E_AUTH_CONFLICT:
      return NVRAM_RESULT_INVALID_PARAMETER;
    case TPM_E_AREA_LOCKED:
    case TPM_E_READ_ONLY:
    case TPM_E_WRITE_LOCKED:
    case TPM_E_DISABLED_CMD:
      return NVRAM_RESULT_OPERATION_DISABLED;
    case TPM_E_AUTHFAIL:
    case TPM_E_NO_NV_PERMISSION:
    case TPM_E_WRONGPCRVAL:
      return NVRAM_RESULT_ACCESS_DENIED;
    case TPM_E_NOSPACE:
    case TPM_E_RESOURCES:
    case TPM_E_SIZE:
      return NVRAM_RESULT_INSUFFICIENT_SPACE;
    case TPM_E_BADINDEX:
    case TPM_E_BAD_HANDLE:
      return NVRAM_RESULT_SPACE_DOES_NOT_EXIST;
  }
  return NVRAM_RESULT_DEVICE_ERROR;
}

}  // namespace

TpmNvramImpl::TpmNvramImpl(LocalDataStore* local_data_store)
    : local_data_store_(local_data_store) {}

NvramResult TpmNvramImpl::DefineSpace(
    uint32_t index,
    size_t size,
    const std::vector<NvramSpaceAttribute>& attributes,
    const std::string& authorization_value,
    NvramSpacePolicy policy) {
  std::string owner_password;
  if (!GetOwnerPassword(&owner_password)) {
    return NVRAM_RESULT_OPERATION_DISABLED;
  }
  TpmConnection owner_connection(owner_password);
  ScopedTssNvStore nv_handle(owner_connection.GetContext());
  if (!InitializeNvramHandle(index, &nv_handle, &owner_connection)) {
    return NVRAM_RESULT_DEVICE_ERROR;
  }
  TSS_RESULT result;
  result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_DATASIZE, 0, size);
  if (TPM_ERROR(result)) {
    TPM_LOG(ERROR, result) << "Could not set size on NVRAM object: " << size;
    return NVRAM_RESULT_DEVICE_ERROR;
  }
  // Set permissions attributes.
  result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_PERMISSIONS, 0,
                                MapAttributesToTpm(attributes));
  if (TPM_ERROR(result)) {
    TPM_LOG(ERROR, result) << "Could not set permissions on NVRAM object";
    return NVRAM_RESULT_DEVICE_ERROR;
  }
  // Set authorization.
  if (!authorization_value.empty() &&
      !SetUsagePolicy(authorization_value, &nv_handle, &owner_connection)) {
    return NVRAM_RESULT_DEVICE_ERROR;
  }
  // Bind to PCR0.
  TSS_HPCRS pcr_handle = 0;
  ScopedTssPcrs scoped_pcr_handle(owner_connection.GetContext());
  if (policy == NVRAM_POLICY_PCR0) {
    if (!SetCompositePcr0(&scoped_pcr_handle, &owner_connection)) {
      return NVRAM_RESULT_DEVICE_ERROR;
    }
    pcr_handle = scoped_pcr_handle;
  }
  result = Tspi_NV_DefineSpace(nv_handle, pcr_handle, /*Read*/
                               pcr_handle /*Write*/);
  if (TPM_ERROR(result)) {
    TPM_LOG(ERROR, result) << "Could not define NVRAM space: " << index;
    return MapTpmError(result);
  }
  return NVRAM_RESULT_SUCCESS;
}

NvramResult TpmNvramImpl::DestroySpace(uint32_t index) {
  std::string owner_password;
  if (!GetOwnerPassword(&owner_password)) {
    return NVRAM_RESULT_OPERATION_DISABLED;
  }
  TpmConnection owner_connection(owner_password);
  ScopedTssNvStore nv_handle(owner_connection.GetContext());
  if (!InitializeNvramHandle(index, &nv_handle, &owner_connection)) {
    return NVRAM_RESULT_DEVICE_ERROR;
  }
  TSS_RESULT result = Tspi_NV_ReleaseSpace(nv_handle);
  if (TPM_ERROR(result)) {
    TPM_LOG(ERROR, result) << "Could not release NVRAM space: " << index;
    return MapTpmError(result);
  }
  return NVRAM_RESULT_SUCCESS;
}

NvramResult TpmNvramImpl::WriteSpace(uint32_t index,
                                     const std::string& data,
                                     const std::string& authorization_value) {
  std::vector<NvramSpaceAttribute> attributes;
  NvramResult result =
      GetSpaceInfo(index, nullptr, nullptr, nullptr, &attributes, nullptr);
  if (result != NVRAM_RESULT_SUCCESS) {
    return result;
  }
  ScopedTssNvStore nv_handle(tpm_connection_.GetContext());
  if (!InitializeNvramHandle(index, &nv_handle, &tpm_connection_)) {
    return NVRAM_RESULT_DEVICE_ERROR;
  }
  for (const auto& attribute : attributes) {
    if (attribute == NVRAM_OWNER_WRITE) {
      if (!SetOwnerPolicy(&nv_handle)) {
        return NVRAM_RESULT_OPERATION_DISABLED;
      }
      break;
    }
    if (attribute == NVRAM_WRITE_AUTHORIZATION) {
      if (!SetUsagePolicy(authorization_value, &nv_handle, &tpm_connection_)) {
        return NVRAM_RESULT_DEVICE_ERROR;
      }
      break;
    }
  }
  TSS_RESULT tpm_result = Tspi_NV_WriteValue(
      nv_handle, 0 /* offset */, data.size(),
      reinterpret_cast<BYTE*>(const_cast<char*>(data.data())));
  if (TPM_ERROR(tpm_result)) {
    TPM_LOG(ERROR, tpm_result) << "Could not write to NVRAM space: " << index;
    return MapTpmError(tpm_result);
  }
  return NVRAM_RESULT_SUCCESS;
}

NvramResult TpmNvramImpl::ReadSpace(uint32_t index,
                                    std::string* data,
                                    const std::string& authorization_value) {
  CHECK(data);
  size_t nvram_size;
  std::vector<NvramSpaceAttribute> attributes;
  NvramResult result =
      GetSpaceInfo(index, &nvram_size, nullptr, nullptr, &attributes, nullptr);
  if (result != NVRAM_RESULT_SUCCESS) {
    return result;
  }
  ScopedTssNvStore nv_handle(tpm_connection_.GetContext());
  if (!InitializeNvramHandle(index, &nv_handle, &tpm_connection_)) {
    return NVRAM_RESULT_DEVICE_ERROR;
  }
  for (const auto& attribute : attributes) {
    if (attribute == NVRAM_OWNER_READ) {
      if (!SetOwnerPolicy(&nv_handle)) {
        return NVRAM_RESULT_OPERATION_DISABLED;
      }
      break;
    }
    if (attribute == NVRAM_READ_AUTHORIZATION) {
      if (!SetUsagePolicy(authorization_value, &nv_handle, &tpm_connection_)) {
        return NVRAM_RESULT_DEVICE_ERROR;
      }
      break;
    }
  }
  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::min(nvram_size - offset, kMaxDataSize);
    ScopedTssMemory space_data(tpm_connection_.GetContext());
    TSS_RESULT tpm_result =
        Tspi_NV_ReadValue(nv_handle, offset, &chunk_size, space_data.ptr());
    if (TPM_ERROR(tpm_result)) {
      TPM_LOG(ERROR, tpm_result) << "Could not read from NVRAM space: "
                                 << index;
      data->clear();
      return MapTpmError(tpm_result);
    }
    if (!space_data.value()) {
      LOG(ERROR) << "No data read from NVRAM space: " << index;
      data->clear();
      return NVRAM_RESULT_DEVICE_ERROR;
    }
    CHECK_LE((offset + chunk_size), data->size());
    data->replace(offset, chunk_size,
                  reinterpret_cast<char*>(space_data.value()), chunk_size);
    offset += chunk_size;
  }
  return NVRAM_RESULT_SUCCESS;
}

NvramResult TpmNvramImpl::LockSpace(uint32_t index,
                                    bool lock_read,
                                    bool lock_write,
                                    const std::string& authorization_value) {
  std::vector<NvramSpaceAttribute> attributes;
  NvramResult result =
      GetSpaceInfo(index, nullptr, nullptr, nullptr, &attributes, nullptr);
  if (result != NVRAM_RESULT_SUCCESS) {
    return result;
  }
  if (lock_read) {
    ScopedTssNvStore nv_handle(tpm_connection_.GetContext());
    if (!InitializeNvramHandle(index, &nv_handle, &tpm_connection_)) {
      return NVRAM_RESULT_DEVICE_ERROR;
    }
    for (const auto& attribute : attributes) {
      if (attribute == NVRAM_OWNER_READ) {
        if (!SetOwnerPolicy(&nv_handle)) {
          return NVRAM_RESULT_OPERATION_DISABLED;
        }
        break;
      }
      if (attribute == NVRAM_READ_AUTHORIZATION) {
        if (!SetUsagePolicy(authorization_value, &nv_handle,
                            &tpm_connection_)) {
          return NVRAM_RESULT_DEVICE_ERROR;
        }
        break;
      }
    }
    uint32_t size = 0;
    ScopedTssMemory space_data(tpm_connection_.GetContext());
    TSS_RESULT tpm_result =
        Tspi_NV_ReadValue(nv_handle, 0, &size, space_data.ptr());
    if (TPM_ERROR(tpm_result)) {
      TPM_LOG(ERROR, tpm_result) << "Could not lock read for NVRAM space: "
                                 << index;
      return MapTpmError(tpm_result);
    }
  }
  if (lock_write) {
    ScopedTssNvStore nv_handle(tpm_connection_.GetContext());
    if (!InitializeNvramHandle(index, &nv_handle, &tpm_connection_)) {
      return NVRAM_RESULT_DEVICE_ERROR;
    }
    for (const auto& attribute : attributes) {
      if (attribute == NVRAM_OWNER_WRITE) {
        if (!SetOwnerPolicy(&nv_handle)) {
          return NVRAM_RESULT_OPERATION_DISABLED;
        }
        break;
      }
      if (attribute == NVRAM_WRITE_AUTHORIZATION) {
        if (!SetUsagePolicy(authorization_value, &nv_handle,
                            &tpm_connection_)) {
          return NVRAM_RESULT_DEVICE_ERROR;
        }
        break;
      }
    }
    BYTE not_used;
    TSS_RESULT tpm_result = Tspi_NV_WriteValue(nv_handle, 0, 0, &not_used);
    if (TPM_ERROR(tpm_result)) {
      TPM_LOG(ERROR, tpm_result) << "Could not lock write for NVRAM space: "
                                 << index;
      return MapTpmError(tpm_result);
    }
  }
  return NVRAM_RESULT_SUCCESS;
}

NvramResult TpmNvramImpl::ListSpaces(std::vector<uint32_t>* index_list) {
  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,
                             nullptr, &nv_list_data_length, nv_list_data.ptr());
  if (TPM_ERROR(result)) {
    TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetCapability";
    return MapTpmError(result);
  }
  // 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);
  for (uint32_t i = 0; i < nv_list_length; ++i) {
    // TPM data is network byte order.
    index_list->push_back(ntohl(nv_list[i]));
  }
  return NVRAM_RESULT_SUCCESS;
}

NvramResult TpmNvramImpl::GetSpaceInfo(
    uint32_t index,
    size_t* size,
    bool* is_read_locked,
    bool* is_write_locked,
    std::vector<NvramSpaceAttribute>* attributes,
    NvramSpacePolicy* policy) {
  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 MapTpmError(result);
  }
  UINT64 offset = 0;
  Trspi_UnloadBlob_NV_DATA_PUBLIC(&offset, nv_index_data.value(), nullptr);
  if (nv_index_data_length < offset) {
    LOG(ERROR) << "Not enough data from Tspi_TPM_GetCapability.";
    return NVRAM_RESULT_DEVICE_ERROR;
  }
  TPM_NV_DATA_PUBLIC info;
  offset = 0;
  result =
      Trspi_UnloadBlob_NV_DATA_PUBLIC(&offset, nv_index_data.value(), &info);
  if (TPM_ERROR(result)) {
    TPM_LOG(ERROR, result) << "Error calling Trspi_UnloadBlob_NV_DATA_PUBLIC";
    return NVRAM_RESULT_DEVICE_ERROR;
  }
  if (size) {
    *size = info.dataSize;
  }
  if (is_read_locked) {
    *is_read_locked = info.bReadSTClear;
  }
  if (is_write_locked) {
    *is_write_locked = info.bWriteSTClear || info.bWriteDefine;
  }
  if (attributes) {
    MapAttributesFromTpm(info.permission.attributes, attributes);
  }
  if (policy) {
    if (info.pcrInfoWrite.pcrSelection.sizeOfSelect > 0 &&
        (info.pcrInfoWrite.pcrSelection.pcrSelect[0] & 1) != 0) {
      *policy = NVRAM_POLICY_PCR0;
    } else {
      *policy = NVRAM_POLICY_NONE;
    }
  }
  return NVRAM_RESULT_SUCCESS;
}

bool TpmNvramImpl::InitializeNvramHandle(uint32_t index,
                                         ScopedTssNvStore* nv_handle,
                                         TpmConnection* connection) {
  TSS_RESULT result = Tspi_Context_CreateObject(
      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) {
  std::string owner_password;
  if (!GetOwnerPassword(&owner_password)) {
    return false;
  }
  return SetUsagePolicy(owner_password, nv_handle, &tpm_connection_);
}

bool TpmNvramImpl::SetUsagePolicy(const std::string& authorization_value,
                                  trousers::ScopedTssNvStore* nv_handle,
                                  TpmConnection* connection) {
  trousers::ScopedTssPolicy policy_handle(connection->GetContext());
  TSS_RESULT result;
  result = Tspi_Context_CreateObject(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;
  }
  result = Tspi_Policy_SetSecret(
      policy_handle, TSS_SECRET_MODE_PLAIN, authorization_value.size(),
      reinterpret_cast<BYTE*>(const_cast<char*>(authorization_value.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,
                                    TpmConnection* connection) {
  TSS_RESULT result = Tspi_Context_CreateObject(
      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;
  ScopedTssMemory pcr_value(connection->GetContext());
  result = Tspi_TPM_PcrRead(connection->GetTpm(), 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