blob: 83717b965d8687ceb3d7d254d36c583b66fac263 [file] [log] [blame]
// Copyright (c) 2009-2010 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.
// Contains the implementation of class Tpm
#include "tpm.h"
#include <base/file_util.h>
#include <base/platform_thread.h>
#include <base/time.h>
#include <openssl/rsa.h>
#include <trousers/tss.h>
#include <trousers/trousers.h>
namespace tpm_init {
const char* kWellKnownSrkTmp = "1234567890";
const int kOwnerPasswordLength = 12;
const int kMaxTimeoutRetries = 5;
const char* kTpmCheckEnabledFile = "/sys/class/misc/tpm0/device/enabled";
const char* kTpmCheckOwnedFile = "/sys/class/misc/tpm0/device/owned";
const char* kTpmOwnedFile = "/var/lib/.tpm_owned";
const char* kOpenCryptokiPath = "/var/lib/opencryptoki";
const int kTpmConnectRetries = 10;
const int kTpmConnectIntervalMs = 100;
Tpm::Tpm()
: context_handle_(0),
default_crypto_(new Crypto()),
crypto_(default_crypto_.get()),
owner_password_(),
password_sync_lock_(),
is_disabled_(true),
is_owned_(false) {
}
Tpm::~Tpm() {
Disconnect();
}
bool Tpm::Init() {
// Checking disabled and owned either via sysfs or via TSS calls will block if
// ownership is being taken by another thread or process. So for this to work
// well, Tpm::Init() needs to be called before InitializeTpm() is called. At
// that point, the public API for Tpm only checks these booleans, so other
// threads can check without being blocked. InitializeTpm() will reset the
// is_owned_ bit on success.
is_disabled_ = IsDisabledCheckViaSysfs();
is_owned_ = IsOwnedCheckViaSysfs();
return true;
}
bool Tpm::Connect() {
if (context_handle_ == 0) {
TSS_HCONTEXT context_handle;
if (!OpenAndConnectTpm(&context_handle)) {
return false;
}
context_handle_ = context_handle;
}
return true;
}
bool Tpm::IsConnected() {
return (context_handle_ != 0);
}
void Tpm::Disconnect() {
if (context_handle_) {
Tspi_Context_Close(context_handle_);
context_handle_ = 0;
}
}
int Tpm::GetMaxRsaKeyCount() {
if (context_handle_ == 0) {
return -1;
}
return GetMaxRsaKeyCountForContext(context_handle_);
}
int Tpm::GetMaxRsaKeyCountForContext(TSS_HCONTEXT context_handle) {
int count = -1;
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpm(context_handle, &tpm_handle)) {
return count;
}
UINT32 cap_length = 0;
BYTE* cap = NULL;
UINT32 subcap = TSS_TPMCAP_PROP_MAXKEYS;
if ((result = Tspi_TPM_GetCapability(tpm_handle, TSS_TPMCAP_PROPERTY,
sizeof(subcap),
reinterpret_cast<BYTE*>(&subcap),
&cap_length, &cap))) {
LOG(ERROR) << "Error calling Tspi_TPM_GetCapability: " << result;
return count;
}
if (cap_length == sizeof(int)) {
count = *(reinterpret_cast<int*>(cap));
}
Tspi_Context_FreeMemory(context_handle, cap);
return count;
}
bool Tpm::OpenAndConnectTpm(TSS_HCONTEXT* context_handle) {
TSS_RESULT result;
TSS_HCONTEXT local_context_handle;
if ((result = Tspi_Context_Create(&local_context_handle))) {
LOG(ERROR) << "Error calling Tspi_Context_Create";
return false;
}
for (int i = 0; i < kTpmConnectRetries; i++) {
if ((result = Tspi_Context_Connect(local_context_handle, NULL))) {
if (result == TSS_E_COMM_FAILURE) {
PlatformThread::Sleep(kTpmConnectIntervalMs);
} else {
LOG(ERROR) << "Error calling Tspi_Context_Connect: " << result;
Tspi_Context_Close(local_context_handle);
return false;
}
} else {
break;
}
}
if (result) {
LOG(ERROR) << "Error calling Tspi_Context_Connect: " << result;
Tspi_Context_Close(local_context_handle);
return false;
}
*context_handle = local_context_handle;
return true;
}
bool Tpm::IsDisabledCheckViaSysfs() {
std::string contents;
if (!file_util::ReadFileToString(FilePath(kTpmCheckEnabledFile), &contents)) {
return false;
}
if (contents.size() < 1) {
return false;
}
return (contents[0] == '0');
}
bool Tpm::IsOwnedCheckViaSysfs() {
std::string contents;
if (!file_util::ReadFileToString(FilePath(kTpmCheckOwnedFile), &contents)) {
return false;
}
if (contents.size() < 1) {
return false;
}
return (contents[0] != '0');
}
bool Tpm::IsDisabledCheckViaContext(TSS_HCONTEXT context_handle) {
bool value = true;
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpm(context_handle, &tpm_handle)) {
return value;
}
UINT32 cap_length = 0;
BYTE* cap = NULL;
if ((result = Tspi_TPM_GetCapability(tpm_handle, TSS_TPMCAP_FLAG,
0, NULL, &cap_length, &cap))) {
LOG(ERROR) << "Error calling Tspi_TPM_GetCapability: " << result;
return value;
}
if (cap_length >= (2 * sizeof(int))) {
value = (((*(reinterpret_cast<int*>(cap))) & TPM_PF_DISABLE) != 0);
}
Tspi_Context_FreeMemory(context_handle, cap);
return value;
}
bool Tpm::IsOwnedCheckViaContext(TSS_HCONTEXT context_handle) {
bool value = true;
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpm(context_handle, &tpm_handle)) {
LOG(ERROR) << "Error calling Tspi_Context_GetTpmObject: " << result;
return value;
}
UINT32 cap_length = 0;
BYTE* cap = NULL;
if ((result = Tspi_TPM_GetCapability(tpm_handle, TSS_TPMCAP_FLAG,
0, NULL, &cap_length, &cap))) {
LOG(ERROR) << "Error calling Tspi_TPM_GetCapability: " << result;
return value;
}
if (cap_length >= (2 * sizeof(int))) {
value = (((*(reinterpret_cast<int*>(cap))) & TPM_PF_OWNERSHIP) != 0);
}
Tspi_Context_FreeMemory(context_handle, cap);
return value;
}
bool Tpm::CreateEndorsementKey(TSS_HCONTEXT context_handle) {
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpm(context_handle, &tpm_handle)) {
return false;
}
TSS_HKEY local_key_handle;
TSS_FLAG init_flags = TSS_KEY_TYPE_LEGACY | TSS_KEY_SIZE_2048;
if ((result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_RSAKEY,
init_flags, &local_key_handle))) {
LOG(ERROR) << "Error calling Tspi_Context_CreateObject: " << result;
return false;
}
if ((result = Tspi_TPM_CreateEndorsementKey(tpm_handle, local_key_handle,
NULL))) {
LOG(ERROR) << "Error calling Tspi_TPM_CreateEndorsementKey: " << result;
Tspi_Context_CloseObject(context_handle, local_key_handle);
return false;
}
return true;
}
bool Tpm::IsEndorsementKeyAvailable(TSS_HCONTEXT context_handle) {
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpm(context_handle, &tpm_handle)) {
return false;
}
TSS_HKEY local_key_handle;
if ((result = Tspi_TPM_GetPubEndorsementKey(tpm_handle, false, NULL,
&local_key_handle))) {
LOG(ERROR) << "Error calling Tspi_Context_CreateObject: " << result;
return false;
}
Tspi_Context_CloseObject(context_handle, local_key_handle);
return true;
}
void Tpm::CreateOwnerPassword(SecureBlob* password) {
SecureBlob random(kOwnerPasswordLength);
crypto_->GetSecureRandom(static_cast<unsigned char*>(random.data()),
random.size());
SecureBlob tpm_password(kOwnerPasswordLength);
crypto_->AsciiEncodeToBuffer(random, static_cast<char*>(tpm_password.data()),
tpm_password.size());
password->swap(tpm_password);
}
bool Tpm::TakeOwnership(TSS_HCONTEXT context_handle, int max_timeout_tries) {
SecureBlob owner_password;
CreateOwnerPassword(&owner_password);
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpmWithAuth(context_handle, owner_password, &tpm_handle)) {
return false;
}
TSS_HKEY srk_handle;
TSS_FLAG init_flags = TSS_KEY_TSP_SRK | TSS_KEY_AUTHORIZATION;
if ((result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_RSAKEY,
init_flags, &srk_handle))) {
LOG(ERROR) << "Error calling Tspi_Context_CreateObject: " << result;
return false;
}
TSS_HPOLICY srk_usage_policy;
if ((result = Tspi_GetPolicyObject(srk_handle, TSS_POLICY_USAGE,
&srk_usage_policy))) {
LOG(ERROR) << "Error calling Tspi_GetPolicyObject: " << result;
Tspi_Context_CloseObject(context_handle, srk_handle);
return false;
}
if ((result = Tspi_Policy_SetSecret(srk_usage_policy,
TSS_SECRET_MODE_PLAIN,
strlen(kWellKnownSrkTmp),
const_cast<BYTE *>(reinterpret_cast<const BYTE *>(kWellKnownSrkTmp))))) {
LOG(ERROR) << "Error calling Tspi_Policy_SetSecret: " << result;
Tspi_Context_CloseObject(context_handle, srk_handle);
return false;
}
int retry_count = 0;
do {
result = Tspi_TPM_TakeOwnership(tpm_handle, 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 < max_timeout_tries));
if (result) {
LOG(ERROR) << "Error calling Tspi_TPM_TakeOwnership: " << result
<< ", attempts: " << retry_count;
Tspi_Context_CloseObject(context_handle, srk_handle);
return false;
}
Tspi_Context_CloseObject(context_handle, srk_handle);
password_sync_lock_.Acquire();
owner_password_.swap(owner_password);
password_sync_lock_.Release();
return true;
}
bool Tpm::ZeroSrkPassword(TSS_HCONTEXT context_handle,
const SecureBlob& owner_password) {
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpmWithAuth(context_handle, owner_password, &tpm_handle)) {
return false;
}
TSS_HKEY srk_handle;
TSS_UUID SRK_UUID = TSS_UUID_SRK;
if ((result = Tspi_Context_LoadKeyByUUID(context_handle, TSS_PS_TYPE_SYSTEM,
SRK_UUID, &srk_handle))) {
LOG(ERROR) << "Couldn't load SRK: " << result;
return false;
}
TSS_HPOLICY policy_handle;
if ((result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_POLICY,
TSS_POLICY_USAGE,
&policy_handle))) {
LOG(ERROR) << "Error creating policy object: " << result;
Tspi_Context_CloseObject(context_handle, srk_handle);
return false;
}
BYTE new_password[0];
if ((result = Tspi_Policy_SetSecret(policy_handle, TSS_SECRET_MODE_PLAIN,
0, new_password))) {
LOG(ERROR) << "Error setting srk password: " << result;
Tspi_Context_CloseObject(context_handle, policy_handle);
Tspi_Context_CloseObject(context_handle, srk_handle);
return false;
}
if ((result = Tspi_ChangeAuth(srk_handle,
tpm_handle,
policy_handle))) {
LOG(ERROR) << "Error creating policy object: " << result;
Tspi_Context_CloseObject(context_handle, policy_handle);
Tspi_Context_CloseObject(context_handle, srk_handle);
return false;
}
Tspi_Context_CloseObject(context_handle, policy_handle);
Tspi_Context_CloseObject(context_handle, srk_handle);
return true;
}
bool Tpm::UnrestrictSrk(TSS_HCONTEXT context_handle,
const SecureBlob& owner_password) {
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpmWithAuth(context_handle, owner_password, &tpm_handle)) {
return false;
}
if ((result = Tspi_TPM_SetStatus(tpm_handle,
TSS_TPMSTATUS_DISABLEPUBSRKREAD,
false))) {
LOG(ERROR) << "Error calling Tspi_TPM_SetStatus: " << result;
return false;
}
return true;
}
bool Tpm::GetTpm(TSS_HCONTEXT context_handle, TSS_HTPM* tpm_handle) {
TSS_RESULT result;
TSS_HTPM local_tpm_handle;
if ((result = Tspi_Context_GetTpmObject(context_handle, &local_tpm_handle))) {
LOG(ERROR) << "Error calling Tspi_Context_GetTpmObject: " << result;
return false;
}
*tpm_handle = local_tpm_handle;
return true;
}
bool Tpm::GetTpmWithAuth(TSS_HCONTEXT context_handle,
const SecureBlob& owner_password,
TSS_HTPM* tpm_handle) {
TSS_RESULT result;
TSS_HTPM local_tpm_handle;
if (!GetTpm(context_handle, &local_tpm_handle)) {
LOG(ERROR) << "Error getting TPM handle";
return false;
}
TSS_HPOLICY tpm_usage_policy;
if ((result = Tspi_GetPolicyObject(local_tpm_handle, TSS_POLICY_USAGE,
&tpm_usage_policy))) {
LOG(ERROR) << "Error calling Tspi_GetPolicyObject: " << result;
return false;
}
if ((result = Tspi_Policy_SetSecret(tpm_usage_policy, TSS_SECRET_MODE_PLAIN,
owner_password.size(),
const_cast<BYTE *>(static_cast<const BYTE *>(
owner_password.const_data()))))) {
LOG(ERROR) << "Error calling Tspi_Policy_SetSecret: " << result;
return false;
}
*tpm_handle = local_tpm_handle;
return true;
}
bool Tpm::GetOwnerPassword(chromeos::Blob* owner_password) {
bool result = false;
if (password_sync_lock_.Try()) {
if (owner_password_.size() != 0) {
owner_password->assign(owner_password_.begin(), owner_password_.end());
result = true;
}
password_sync_lock_.Release();
}
return result;
}
bool Tpm::InitializeTpm() {
if (!IsConnected()) {
Connect();
}
if (!IsConnected()) {
LOG(ERROR) << "Failed to connect to TPM";
return false;
}
if (is_disabled_) {
LOG(ERROR) << "Error TPM is disabled";
return false;
}
if (is_owned_) {
return false;
}
file_util::Delete(FilePath(kOpenCryptokiPath), true);
file_util::Delete(FilePath(kTpmOwnedFile), false);
if (!IsEndorsementKeyAvailable(context_handle_)) {
if (!CreateEndorsementKey(context_handle_)) {
LOG(ERROR) << "Failed to create endorsement key";
return false;
}
}
if (!IsEndorsementKeyAvailable(context_handle_)) {
LOG(ERROR) << "Endorsement key is not available";
return false;
}
if (!TakeOwnership(context_handle_, kMaxTimeoutRetries)) {
LOG(ERROR) << "Take Ownership failed";
return false;
}
SecureBlob owner_password;
password_sync_lock_.Acquire();
owner_password.assign(owner_password_.begin(), owner_password_.end());
password_sync_lock_.Release();
if (!ZeroSrkPassword(context_handle_, owner_password)) {
LOG(ERROR) << "Couldn't zero SRK password";
return false;
}
if (!UnrestrictSrk(context_handle_, owner_password)) {
LOG(ERROR) << "Couldn't unrestrict the SRK";
return false;
}
is_owned_ = true;
file_util::WriteFile(FilePath(kTpmOwnedFile), NULL, 0);
return true;
}
} // namespace tpm_init