diff options
author | Utkarsh Sanghi <usanghi@chromium.org> | 2015-08-31 12:18:55 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2015-09-09 11:18:01 -0700 |
commit | 50e52ff6bcc478118a1cdec27903a5af5061d77b (patch) | |
tree | 6b49512635ddd192c10077add62efcf6f5dd9cd7 | |
parent | 565f88fe8ebdabefe07e5d5cb65ae9bcab509368 (diff) | |
download | tpm_manager-50e52ff6bcc478118a1cdec27903a5af5061d77b.tar.gz |
tpm_manager: Implement Ownership flow
This CL implements the TakeOwnership IPC interface in TpmManager.
This allows clients to take ownership of a Tpm by injecting a random
password. Once a Tpm is owned, it can be utilized by ChromeOS.
BUG=chromium:521635
TEST=unit and take ownership on DUT
Change-Id: Icd4de6b6b1be419dd035e535473929cb8c0ecb16
Reviewed-on: https://chromium-review.googlesource.com/294614
Commit-Ready: Utkarsh Sanghi <usanghi@chromium.org>
Tested-by: Utkarsh Sanghi <usanghi@chromium.org>
Reviewed-by: Utkarsh Sanghi <usanghi@chromium.org>
-rw-r--r-- | client/main.cc | 22 | ||||
-rw-r--r-- | common/local_data.proto | 11 | ||||
-rw-r--r-- | server/main.cc | 12 | ||||
-rw-r--r-- | server/openssl_crypto_util.cc | 26 | ||||
-rw-r--r-- | server/openssl_crypto_util.h | 36 | ||||
-rw-r--r-- | server/tpm2_initializer_impl.cc | 121 | ||||
-rw-r--r-- | server/tpm2_initializer_impl.h | 66 | ||||
-rw-r--r-- | server/tpm2_initializer_test.cc | 138 | ||||
-rw-r--r-- | server/tpm_connection.cc | 35 | ||||
-rw-r--r-- | server/tpm_connection.h | 13 | ||||
-rw-r--r-- | server/tpm_initializer_impl.cc | 246 | ||||
-rw-r--r-- | server/tpm_initializer_impl.h | 80 | ||||
-rw-r--r-- | server/tpm_manager-seccomp-amd64.policy | 3 | ||||
-rw-r--r-- | server/tpm_manager_service.cc | 5 | ||||
-rw-r--r-- | server/tpm_manager_service.h | 12 | ||||
-rw-r--r-- | server/tpm_manager_service_test.cc | 7 | ||||
-rw-r--r-- | server/tpm_status_impl.cc | 2 | ||||
-rw-r--r-- | server/tpm_util.h | 16 | ||||
-rw-r--r-- | tpm_manager.gyp | 5 |
19 files changed, 831 insertions, 25 deletions
diff --git a/client/main.cc b/client/main.cc index faa62a5..376e0aa 100644 --- a/client/main.cc +++ b/client/main.cc @@ -22,11 +22,14 @@ namespace tpm_manager { const char kGetTpmStatusCommand[] = "status"; +const char kTakeOwnershipCommand[] = "take_ownership"; const char kUsage[] = R"( Usage: tpm_manager_client <command> [<args>] Commands: status Prints the current status of the Tpm. + take_ownership + Takes ownership of the Tpm with a random password. )"; using ClientLoopBase = chromeos::Daemon; @@ -69,6 +72,9 @@ class ClientLoop : public ClientLoopBase { } else if (command_line->HasSwitch(kGetTpmStatusCommand)) { task = base::Bind(&ClientLoop::HandleGetTpmStatus, weak_factory_.GetWeakPtr()); + } else if (command_line->HasSwitch(kTakeOwnershipCommand)) { + task = base::Bind(&ClientLoop::HandleTakeOwnership, + weak_factory_.GetWeakPtr()); } else { // Command line arguments did not match any valid commands. LOG(ERROR) << "No Valid Command selected."; @@ -95,6 +101,22 @@ class ClientLoop : public ClientLoopBase { weak_factory_.GetWeakPtr())); } + void PrintTakeOwnershipReply(const TakeOwnershipReply& reply) { + if (reply.has_status() && reply.status() == STATUS_NOT_AVAILABLE) { + LOG(INFO) << "tpm_managerd is not available."; + } else { + LOG(INFO) << "TakeOwnershipReply: " << GetProtoDebugString(reply); + } + Quit(); + } + + void HandleTakeOwnership() { + TakeOwnershipRequest request; + tpm_manager_->TakeOwnership(request, + base::Bind(&ClientLoop::PrintTakeOwnershipReply, + weak_factory_.GetWeakPtr())); + } + // Pointer to a DBus proxy to tpm_managerd. std::unique_ptr<tpm_manager::TpmManagerInterface> tpm_manager_; diff --git a/common/local_data.proto b/common/local_data.proto index efcca47..509f027 100644 --- a/common/local_data.proto +++ b/common/local_data.proto @@ -1,9 +1,20 @@ +// Copyright 2015 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. + option optimize_for = LITE_RUNTIME; package tpm_manager; // The format of persistent local TPM management data stored on the device. +// When Tpm ownership is taken, this protobuf is populated with the passwords +// used to take ownership, and with a list of clients who have a dependency on +// the owner password (like Attestation, InstallAttributes and BootLockbox). +// when all the clients have the owner password injected, this protobuf is +// cleared of all passwords. message LocalData { optional bool owned_by_this_install = 1; optional bytes owner_password = 2; repeated string owner_dependency = 3; + optional bytes endorsement_password = 4; + optional bytes lockout_password = 5; } diff --git a/server/main.cc b/server/main.cc index 50063f3..f7dcc45 100644 --- a/server/main.cc +++ b/server/main.cc @@ -18,8 +18,10 @@ #include "tpm_manager/server/tpm_manager_service.h" #if USE_TPM2 +#include "tpm_manager/server/tpm2_initializer_impl.h" #include "tpm_manager/server/tpm2_status_impl.h" #else +#include "tpm_manager/server/tpm_initializer_impl.h" #include "tpm_manager/server/tpm_status_impl.h" #endif @@ -37,13 +39,20 @@ class TpmManagerDaemon : public chromeos::DBusServiceDaemon { local_data_store_.reset(new tpm_manager::LocalDataStoreImpl()); #if USE_TPM2 tpm_status_.reset(new tpm_manager::Tpm2StatusImpl); + tpm_initializer_.reset(new tpm_manager::Tpm2InitializerImpl( + local_data_store_.get(), + tpm_status_.get())); #else tpm_status_.reset(new tpm_manager::TpmStatusImpl); + tpm_initializer_.reset(new tpm_manager::TpmInitializerImpl( + local_data_store_.get(), + tpm_status_.get())); #endif tpm_manager_service_.reset(new tpm_manager::TpmManagerService( command_line->HasSwitch(kWaitForOwnershipTriggerSwitch), local_data_store_.get(), - tpm_status_.get())); + tpm_status_.get(), + tpm_initializer_.get())); } protected: @@ -66,6 +75,7 @@ class TpmManagerDaemon : public chromeos::DBusServiceDaemon { private: std::unique_ptr<tpm_manager::LocalDataStore> local_data_store_; std::unique_ptr<tpm_manager::TpmStatus> tpm_status_; + std::unique_ptr<tpm_manager::TpmInitializer> tpm_initializer_; std::unique_ptr<tpm_manager::TpmManagerInterface> tpm_manager_service_; std::unique_ptr<tpm_manager::DBusService> dbus_service_; diff --git a/server/openssl_crypto_util.cc b/server/openssl_crypto_util.cc new file mode 100644 index 0000000..0edaf05 --- /dev/null +++ b/server/openssl_crypto_util.cc @@ -0,0 +1,26 @@ +// Copyright 2015 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. + +#include "tpm_manager/server/openssl_crypto_util.h" + +#include <base/logging.h> +#include <base/stl_util.h> +#include <openssl/rand.h> + +namespace tpm_manager { + +bool OpensslCryptoUtil::GetRandomBytes(size_t num_bytes, + std::string* random_data) { + random_data->resize(num_bytes); + unsigned char* random_buffer = + reinterpret_cast<unsigned char*>(string_as_array(random_data)); + if (RAND_bytes(random_buffer, num_bytes) != 1) { + LOG(ERROR) << "Error getting random bytes using Openssl."; + random_data->clear(); + return false; + } + return true; +} + +} // namespace tpm_manager diff --git a/server/openssl_crypto_util.h b/server/openssl_crypto_util.h new file mode 100644 index 0000000..f252e57 --- /dev/null +++ b/server/openssl_crypto_util.h @@ -0,0 +1,36 @@ +// Copyright 2015 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. + +#ifndef TPM_MANAGER_SERVER_OPENSSL_CRYPTO_UTIL_H_ +#define TPM_MANAGER_SERVER_OPENSSL_CRYPTO_UTIL_H_ + +#include <string> + +#include <base/compiler_specific.h> +#include <base/macros.h> + +namespace tpm_manager { + +// This class is used to provide a mockable interface for openssl calls. +// Example usage: +// OpensslCryptoUtil util; +// std::string random_bytes; +// bool result = util.GetRandomBytes(5, &random_bytes); +class OpensslCryptoUtil { + public: + OpensslCryptoUtil() = default; + ~OpensslCryptoUtil() = default; + + // This method sets the out argument |random_data| to a string with at + // least |num_bytes| of random data and returns true on success. + bool GetRandomBytes(size_t num_bytes, + std::string* random_data) WARN_UNUSED_RESULT; + + private: + DISALLOW_COPY_AND_ASSIGN(OpensslCryptoUtil); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_OPENSSL_CRYPTO_UTIL_H_ diff --git a/server/tpm2_initializer_impl.cc b/server/tpm2_initializer_impl.cc new file mode 100644 index 0000000..63b4a66 --- /dev/null +++ b/server/tpm2_initializer_impl.cc @@ -0,0 +1,121 @@ +// Copyright 2015 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. + +#include "tpm_manager/server/tpm2_initializer_impl.h" + +#include <string> + +#include <base/logging.h> +#include <trunks/tpm_utility.h> +#include <trunks/trunks_factory_impl.h> + +#include "tpm_manager/common/local_data.pb.h" + +namespace { +const size_t kDefaultPasswordSize = 20; +} // namespace + +namespace tpm_manager { + +Tpm2InitializerImpl::Tpm2InitializerImpl(LocalDataStore* local_data_store, + TpmStatus* tpm_status) + : trunks_factory_(new trunks::TrunksFactoryImpl()), + openssl_util_(new OpensslCryptoUtil()), + local_data_store_(local_data_store), + tpm_status_(tpm_status) {} + +Tpm2InitializerImpl::Tpm2InitializerImpl(trunks::TrunksFactory* factory, + OpensslCryptoUtil* openssl_util, + LocalDataStore* local_data_store, + TpmStatus* tpm_status) + : trunks_factory_(factory), + openssl_util_(openssl_util), + local_data_store_(local_data_store), + tpm_status_(tpm_status) {} + +bool Tpm2InitializerImpl::InitializeTpm() { + if (!SeedTpmRng()) { + return false; + } + if (tpm_status_->IsTpmOwned()) { + // Tpm is already owned, so we do not need to do anything. + VLOG(1) << "Tpm already owned."; + return true; + } + // First we read the local data. If the |owned_by_this_install| flag is set, + // either we did not finish removing owner dependencies or TakeOwnership + // failed. In either case, we want to retake ownership with the same + // passwords. + LocalData local_data; + if (!local_data_store_->Read(&local_data)) { + LOG(ERROR) << "Error reading local data."; + return false; + } + std::string owner_password; + std::string endorsement_password; + std::string lockout_password; + // TODO(usanghi): Use owner dependency information rather than the + // |owned_by_this_install| flag here. + if (local_data.owned_by_this_install()) { + owner_password.assign(local_data.owner_password()); + endorsement_password.assign(local_data.endorsement_password()); + lockout_password.assign(local_data.lockout_password()); + } else { + if (!GetTpmRandomData(kDefaultPasswordSize, &owner_password)) { + LOG(ERROR) << "Error generating a random owner password."; + return false; + } + if (!GetTpmRandomData(kDefaultPasswordSize, &endorsement_password)) { + LOG(ERROR) << "Error generating a random endorsement password."; + return false; + } + if (!GetTpmRandomData(kDefaultPasswordSize, &lockout_password)) { + LOG(ERROR) << "Error generating a random lockout password."; + return false; + } + } + // We write the passwords to disk, in case there is an error while taking + // ownership. + local_data.set_owned_by_this_install(true); + local_data.set_owner_password(owner_password); + local_data.set_endorsement_password(endorsement_password); + local_data.set_lockout_password(lockout_password); + // TODO(usanghi): Add ownership dependencies here. + if (!local_data_store_->Write(local_data)) { + LOG(ERROR) << "Error saving local data."; + return false; + } + trunks::TPM_RC result = trunks_factory_->GetTpmUtility()->TakeOwnership( + owner_password, endorsement_password, lockout_password); + if (result != trunks::TPM_RC_SUCCESS) { + LOG(ERROR) << "Error taking ownership of TPM2.0"; + return false; + } + return true; +} + +bool Tpm2InitializerImpl::SeedTpmRng() { + std::string random_bytes; + if (!openssl_util_->GetRandomBytes(kDefaultPasswordSize, &random_bytes)) { + return false; + } + trunks::TPM_RC result = trunks_factory_->GetTpmUtility()->StirRandom( + random_bytes, nullptr /* No Authorization */); + if (result != trunks::TPM_RC_SUCCESS) { + return false; + } + return true; +} + +bool Tpm2InitializerImpl::GetTpmRandomData(size_t num_bytes, + std::string* random_data) { + trunks::TPM_RC result = trunks_factory_->GetTpmUtility()->GenerateRandom( + num_bytes, nullptr /* No Authorization */, random_data); + if (result != trunks::TPM_RC_SUCCESS) { + return false; + } + return true; +} + +} // namespace tpm_manager diff --git a/server/tpm2_initializer_impl.h b/server/tpm2_initializer_impl.h new file mode 100644 index 0000000..a50abea --- /dev/null +++ b/server/tpm2_initializer_impl.h @@ -0,0 +1,66 @@ +// Copyright 2015 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. + +#ifndef TPM_MANAGER_SERVER_TPM2_INITIALIZER_IMPL_H_ +#define TPM_MANAGER_SERVER_TPM2_INITIALIZER_IMPL_H_ + +#include "tpm_manager/server/tpm_initializer.h" + +#include <string> +#include <memory> + +#include <base/macros.h> +#include <trunks/trunks_factory.h> + +#include "tpm_manager/server/local_data_store.h" +#include "tpm_manager/server/openssl_crypto_util.h" +#include "tpm_manager/server/tpm_status.h" + +namespace tpm_manager { + +// This class initializes a Tpm2.0 chip by taking ownership. Example use of +// this class is: +// LocalDataStore data_store; +// Tpm2StatusImpl status; +// Tpm2InitializerImpl initializer(&data_store, &status); +// initializer.InitializeTpm(); +// If the tpm is unowned, InitializeTpm injects random owner, endorsement and +// lockout passwords, intializes the SRK with empty authorization, and persists +// the passwords to disk until all the owner dependencies are satisfied. +class Tpm2InitializerImpl : public TpmInitializer { + public: + // Does not take ownership of |local_data_store| or |tpm_status|. + Tpm2InitializerImpl(LocalDataStore* local_data_store, + TpmStatus* tpm_status); + // Does not take ownership of |openssl_util|, |local_data_store| or + // |tpm_status|. Takes ownership of |factory|. + Tpm2InitializerImpl(trunks::TrunksFactory* factory, + OpensslCryptoUtil* openssl_util, + LocalDataStore* local_data_store, + TpmStatus* tpm_status); + ~Tpm2InitializerImpl() override = default; + + // TpmInitializer methods. + bool InitializeTpm() override; + + private: + // Seeds the onboard Tpm random number generator with random bytes from + // Openssl, if the Tpm RNG has not been seeded yet. Returns true on success. + bool SeedTpmRng(); + + // Gets random bytes of length |num_bytes| and populates the string at + // |random_data|. Returns true on success. + bool GetTpmRandomData(size_t num_bytes, std::string* random_data); + + std::unique_ptr<trunks::TrunksFactory> trunks_factory_; + OpensslCryptoUtil* openssl_util_; + LocalDataStore* local_data_store_; + TpmStatus* tpm_status_; + + DISALLOW_COPY_AND_ASSIGN(Tpm2InitializerImpl); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_TPM2_INITIALIZER_IMPL_H_ diff --git a/server/tpm2_initializer_test.cc b/server/tpm2_initializer_test.cc new file mode 100644 index 0000000..9d1bfd0 --- /dev/null +++ b/server/tpm2_initializer_test.cc @@ -0,0 +1,138 @@ +// Copyright 2015 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. + +#include "tpm_manager/server/tpm2_initializer_impl.h" + +#include <memory> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <trunks/mock_tpm_utility.h> +#include <trunks/trunks_factory_for_test.h> + +#include "tpm_manager/server/mock_local_data_store.h" +#include "tpm_manager/server/mock_openssl_crypto_util.h" +#include "tpm_manager/server/mock_tpm_status.h" + +using testing::_; +using testing::DoAll; +using testing::NiceMock; +using testing::Return; +using testing::SaveArg; +using testing::SetArgPointee; + +namespace tpm_manager { + +class Tpm2InitializerTest : public testing::Test { + public: + Tpm2InitializerTest() = default; + virtual ~Tpm2InitializerTest() = default; + + void SetUp() { + trunks::TrunksFactoryForTest* factory = new trunks::TrunksFactoryForTest(); + factory->set_tpm_utility(&mock_tpm_utility_); + tpm_initializer_.reset(new Tpm2InitializerImpl(factory, + &mock_openssl_util_, + &mock_data_store_, + &mock_tpm_status_)); + } + + protected: + NiceMock<MockOpensslCryptoUtil> mock_openssl_util_; + NiceMock<MockLocalDataStore> mock_data_store_; + NiceMock<MockTpmStatus> mock_tpm_status_; + NiceMock<trunks::MockTpmUtility> mock_tpm_utility_; + std::unique_ptr<TpmInitializer> tpm_initializer_; +}; + +TEST_F(Tpm2InitializerTest, InitializeTpmNoSeedTpm) { + EXPECT_CALL(mock_tpm_utility_, StirRandom(_, _)) + .WillRepeatedly(Return(trunks::TPM_RC_FAILURE)); + EXPECT_FALSE(tpm_initializer_->InitializeTpm()); +} + +TEST_F(Tpm2InitializerTest, InitializeTpmAlreadyOwned) { + EXPECT_CALL(mock_tpm_status_, IsTpmOwned()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(mock_tpm_utility_, TakeOwnership(_, _, _)) + .Times(0); + EXPECT_TRUE(tpm_initializer_->InitializeTpm()); +} + +TEST_F(Tpm2InitializerTest, InitializeTpmLocalDataReadError) { + EXPECT_CALL(mock_tpm_status_, IsTpmOwned()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(mock_data_store_, Read(_)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(mock_tpm_utility_, TakeOwnership(_, _, _)) + .Times(0); + EXPECT_FALSE(tpm_initializer_->InitializeTpm()); +} + +TEST_F(Tpm2InitializerTest, InitializeTpmLocalDataWriteError) { + EXPECT_CALL(mock_tpm_status_, IsTpmOwned()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(mock_data_store_, Write(_)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(mock_tpm_utility_, TakeOwnership(_, _, _)) + .Times(0); + EXPECT_FALSE(tpm_initializer_->InitializeTpm()); +} + +TEST_F(Tpm2InitializerTest, InitializeTpmOwnershipError) { + EXPECT_CALL(mock_tpm_status_, IsTpmOwned()) + .WillOnce(Return(false)); + EXPECT_CALL(mock_tpm_utility_, TakeOwnership(_, _, _)) + .WillRepeatedly(Return(trunks::TPM_RC_FAILURE)); + EXPECT_FALSE(tpm_initializer_->InitializeTpm()); +} + +TEST_F(Tpm2InitializerTest, InitializeTpmSuccess) { + EXPECT_CALL(mock_tpm_status_, IsTpmOwned()) + .WillOnce(Return(false)); + std::string owner_password; + std::string endorsement_password; + std::string lockout_password; + LocalData local_data; + local_data.set_owned_by_this_install(false); + EXPECT_CALL(mock_data_store_, Read(_)) + .WillOnce(DoAll(SetArgPointee<0>(local_data), + Return(true))); + EXPECT_CALL(mock_tpm_utility_, GenerateRandom(_, _, _)) + .Times(3) // Once for owner, endorsement and lockout passwords + .WillRepeatedly(Return(trunks::TPM_RC_SUCCESS)); + EXPECT_CALL(mock_tpm_utility_, TakeOwnership(_, _, _)) + .WillOnce(Return(trunks::TPM_RC_SUCCESS)); + EXPECT_TRUE(tpm_initializer_->InitializeTpm()); +} + +TEST_F(Tpm2InitializerTest, InitializeTpmSuccessAfterError) { + EXPECT_CALL(mock_tpm_status_, IsTpmOwned()) + .WillOnce(Return(false)); + std::string owner_password("owner"); + std::string endorsement_password("endorsement"); + std::string lockout_password("lockout"); + LocalData local_data; + local_data.set_owned_by_this_install(true); + local_data.set_owner_password(owner_password); + local_data.set_endorsement_password(endorsement_password); + local_data.set_lockout_password(lockout_password); + EXPECT_CALL(mock_data_store_, Read(_)) + .WillOnce(DoAll(SetArgPointee<0>(local_data), + Return(true))); + EXPECT_CALL(mock_data_store_, Write(_)) + .WillOnce(DoAll(SaveArg<0>(&local_data), + Return(true))); + EXPECT_EQ(true, local_data.owned_by_this_install()); + EXPECT_EQ(owner_password, local_data.owner_password()); + EXPECT_EQ(endorsement_password, local_data.endorsement_password()); + EXPECT_EQ(lockout_password, local_data.lockout_password()); + EXPECT_CALL(mock_tpm_utility_, TakeOwnership(owner_password, + endorsement_password, + lockout_password)) + .WillOnce(Return(trunks::TPM_RC_SUCCESS)); + EXPECT_TRUE(tpm_initializer_->InitializeTpm()); +} + +} // namespace tpm_manager diff --git a/server/tpm_connection.cc b/server/tpm_connection.cc index 7a241a7..ee57f3a 100644 --- a/server/tpm_connection.cc +++ b/server/tpm_connection.cc @@ -5,11 +5,14 @@ #include "tpm_manager/server/tpm_connection.h" #include <base/logging.h> +#include <base/stl_util.h> #include <base/threading/platform_thread.h> #include <base/time/time.h> #include <trousers/tss.h> #include <trousers/trousers.h> // NOLINT(build/include_alpha) +#include "tpm_manager/server/tpm_util.h" + namespace { const int kTpmConnectRetries = 10; @@ -19,9 +22,12 @@ const int kTpmConnectIntervalMs = 100; namespace tpm_manager { -#define TPM_LOG(severity, result) \ - LOG(severity) << "TPM error 0x" << std::hex << result \ - << " (" << Trspi_Error_String(result) << "): " +TSS_HCONTEXT TpmConnection::GetContext() { + if (!ConnectContextIfNeeded()) { + return 0; + } + return context_.value(); +} TSS_HTPM TpmConnection::GetTpm() { if (!ConnectContextIfNeeded()) { @@ -37,11 +43,28 @@ TSS_HTPM TpmConnection::GetTpm() { return tpm_handle; } -TSS_HCONTEXT TpmConnection::GetContext() { - if (!ConnectContextIfNeeded()) { +TSS_HTPM TpmConnection::GetTpmWithAuth(const std::string& owner_password) { + TSS_HTPM tpm_handle = GetTpm(); + if (tpm_handle == 0) { return 0; } - return context_.value(); + TSS_RESULT result; + TSS_HPOLICY tpm_usage_policy; + if (TPM_ERROR(result = Tspi_GetPolicyObject(tpm_handle, + TSS_POLICY_USAGE, + &tpm_usage_policy))) { + TPM_LOG(ERROR, result) << "Error calling Tspi_GetPolicyObject"; + return false; + } + if (TPM_ERROR(result = Tspi_Policy_SetSecret( + tpm_usage_policy, + TSS_SECRET_MODE_PLAIN, + owner_password.size(), + reinterpret_cast<BYTE *>(const_cast<char*>(owner_password.data()))))) { + TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret"; + return false; + } + return tpm_handle; } bool TpmConnection::ConnectContextIfNeeded() { diff --git a/server/tpm_connection.h b/server/tpm_connection.h index a0f8fe5..833a972 100644 --- a/server/tpm_connection.h +++ b/server/tpm_connection.h @@ -5,10 +5,11 @@ #ifndef TPM_MANAGER_SERVER_TPM_CONNECTION_H_ #define TPM_MANAGER_SERVER_TPM_CONNECTION_H_ +#include <string> + #include <base/macros.h> #include <trousers/scoped_tss_type.h> - namespace tpm_manager { class TpmConnection { @@ -16,14 +17,18 @@ class TpmConnection { TpmConnection() = default; ~TpmConnection() = default; - // This method tries to get a handle to the TPM. Returns 0 on failure. - TSS_HTPM GetTpm(); - // This method returns a handle to the current Tpm context. // Note: this method still retains ownership of the context. If this class // is deleted, the context handle will be invalidated. Returns 0 on failure. TSS_HCONTEXT GetContext(); + // This method tries to get a handle to the TPM. Returns 0 on failure. + TSS_HTPM GetTpm(); + + // This method tries to get a handle to the TPM and with the given owner + // password. Returns 0 on failure. + TSS_HTPM GetTpmWithAuth(const std::string& owner_password); + private: // This method connects to the Tpm. Returns true on success. bool ConnectContextIfNeeded(); diff --git a/server/tpm_initializer_impl.cc b/server/tpm_initializer_impl.cc new file mode 100644 index 0000000..a33d5b6 --- /dev/null +++ b/server/tpm_initializer_impl.cc @@ -0,0 +1,246 @@ +// Copyright 2015 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. + +#include "tpm_manager/server/tpm_initializer_impl.h" + +#include <string> + +#include <base/logging.h> +#include <base/stl_util.h> +#include <trousers/scoped_tss_type.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 char kDefaultOwnerPassword[] = TSS_WELL_KNOWN_SECRET; +const size_t kDefaultPasswordSize = 20; +const int kMaxOwnershipTimeoutRetries = 5; +const char* kWellKnownSrkSecret = "well_known_srk_secret"; + +} // namespace + +namespace tpm_manager { + +TpmInitializerImpl::TpmInitializerImpl(LocalDataStore* local_data_store, + TpmStatus* tpm_status) + : local_data_store_(local_data_store), + tpm_status_(tpm_status) {} + +bool TpmInitializerImpl::InitializeTpm() { + if (tpm_status_->IsTpmOwned() && !TestTpmAuth(kDefaultOwnerPassword)) { + // Tpm is already owned, so we do not need to do anything. + VLOG(1) << "Tpm already owned."; + return true; + } + TSS_HTPM tpm_handle = tpm_connection_.GetTpm(); + if (tpm_handle == 0) { + return false; + } + if (!InitializeEndorsementKey(tpm_handle) || + !TakeOwnership(tpm_handle) || + !InitializeSrk(tpm_handle)) { + return false; + } + std::string owner_password; + if (!openssl_util_.GetRandomBytes(kDefaultPasswordSize, &owner_password)) { + return false; + } + LocalData local_data; + local_data.set_owned_by_this_install(true); + local_data.set_owner_password(owner_password); + // TODO(usanghi): Add ownership dependencies here. + if (!local_data_store_->Write(local_data)) { + LOG(ERROR) << "Error saving local data."; + return false; + } + if (!ChangeOwnerPassword(tpm_handle, owner_password)) { + return false; + } + return true; +} + +bool TpmInitializerImpl::InitializeEndorsementKey(TSS_HTPM tpm_handle) { + trousers::ScopedTssKey local_key_handle(tpm_connection_.GetContext()); + TSS_RESULT result = Tspi_TPM_GetPubEndorsementKey(tpm_handle, + 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. + 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(tpm_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(tpm_handle, + local_key_handle, + NULL))) { + TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_CreateEndorsementKey"; + return false; + } + return true; +} + +bool TpmInitializerImpl::TakeOwnership(TSS_HTPM tpm_handle) { + if (TestTpmAuth(kDefaultOwnerPassword)) { + VLOG(1) << "The Tpm already has the default owner password."; + return true; + } + TSS_RESULT result; + trousers::ScopedTssKey srk_handle(tpm_connection_.GetContext()); + TSS_FLAG init_flags = TSS_KEY_TSP_SRK | TSS_KEY_AUTHORIZATION; + if (TPM_ERROR(result = Tspi_Context_CreateObject(tpm_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 potentailly 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(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 < kMaxOwnershipTimeoutRetries)); + if (result) { + TPM_LOG(ERROR, result) + << "Error calling Tspi_TPM_TakeOwnership, attempts: " << retry_count; + return false; + } + return true; +} + +bool TpmInitializerImpl::InitializeSrk(TSS_HTPM tpm_handle) { + TSS_RESULT result; + trousers::ScopedTssKey srk_handle(tpm_connection_.GetContext()); + TSS_UUID SRK_UUID = TSS_UUID_SRK; + if (TPM_ERROR(result = Tspi_Context_LoadKeyByUUID( + tpm_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(tpm_connection_.GetContext()); + if (TPM_ERROR(result = Tspi_Context_CreateObject(tpm_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, + tpm_handle, + 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(tpm_handle, + 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(tpm_handle, + TSS_TPMSTATUS_DISABLEPUBSRKREAD, + false))) { + TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_SetStatus"; + return false; + } + } + return true; +} + +bool TpmInitializerImpl::ChangeOwnerPassword( + TSS_HTPM tpm_handle, const std::string& owner_password) { + TSS_RESULT result; + trousers::ScopedTssPolicy policy_handle(tpm_connection_.GetContext()); + if (TPM_ERROR(result = Tspi_Context_CreateObject(tpm_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 *>(string_as_array(&mutable_owner_password))))) { + TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret"; + return false; + } + + if (TPM_ERROR(result = Tspi_ChangeAuth(tpm_handle, 0, policy_handle))) { + TPM_LOG(ERROR, result) << "Error calling Tspi_ChangeAuth"; + return false; + } + + return true; +} + +bool TpmInitializerImpl::TestTpmAuth(const std::string& owner_password) { + TSS_HTPM tpm_handle = tpm_connection_.GetTpmWithAuth(owner_password); + if (tpm_handle == 0) { + return false; + } + // Call Tspi_TPM_GetStatus to test the |owner_password| provided. + TSS_RESULT result; + TSS_BOOL current_status = false; + if (TPM_ERROR(result = Tspi_TPM_GetStatus(tpm_handle, + TSS_TPMSTATUS_DISABLED, + ¤t_status))) { + return false; + } + return true; +} + +} // namespace tpm_manager diff --git a/server/tpm_initializer_impl.h b/server/tpm_initializer_impl.h new file mode 100644 index 0000000..58adf55 --- /dev/null +++ b/server/tpm_initializer_impl.h @@ -0,0 +1,80 @@ +// Copyright 2015 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. + +#ifndef TPM_MANAGER_SERVER_TPM_INITIALIZER_IMPL_H_ +#define TPM_MANAGER_SERVER_TPM_INITIALIZER_IMPL_H_ + +#include <string> + +#include <base/macros.h> +#include <trousers/tss.h> +#include <trousers/trousers.h> // NOLINT(build/include_alpha) + +#include "tpm_manager/server/openssl_crypto_util.h" +#include "tpm_manager/server/tpm_connection.h" +#include "tpm_manager/server/tpm_initializer.h" + +namespace tpm_manager { + +class LocalDataStore; +class TpmStatus; + +// This class initializes a Tpm1.2 chip by taking ownership. Example use of +// this class is: +// LocalDataStore data_store; +// TpmStatusImpl status; +// TpmInitializerImpl initializer(&data_store, &status); +// initializer.InitializeTpm(); +// If the tpm is unowned, InitializeTpm injects a random owner password, +// initializes and unrestricts the SRK, and persists the owner password to disk +// until all the owner dependencies are satisfied. +class TpmInitializerImpl : public TpmInitializer { + public: + // Does not take ownership of |local_data_store| or |tpm_status|. + TpmInitializerImpl(LocalDataStore* local_data_store, + TpmStatus* tpm_status); + ~TpmInitializerImpl() override = default; + + // TpmInitializer methods. + bool InitializeTpm() override; + + private: + // This method checks if an EndorsementKey exists on the Tpm and creates it + // if not. Returns true on success, else false. |tpm_handle| is a handle to + // the Tpm with the owner_password injected. + bool InitializeEndorsementKey(TSS_HTPM tpm_handle); + + // This method takes ownership of the Tpm with the default TSS password. + // Returns true on success, else false. |tpm_handle| is a handle to the Tpm + // with the owner_password injected. + bool TakeOwnership(TSS_HTPM tpm_handle); + + // This method initializes the SRK if it does not exist, zero's the SRK + // password and unrestricts its usage. Returns true on success, else false. + // |tpm_handle| is a handle to the Tpm with the owner_password injected. + bool InitializeSrk(TSS_HTPM tpm_handle); + + // This method changes the Tpm owner password from the default TSS password + // to the password provided in the |owner_password| argument. + // Returns true on success, else false. |tpm_handle| is a handle to the Tpm + // with the old owner_password injected. + bool ChangeOwnerPassword(TSS_HTPM tpm_handle, + const std::string& owner_password); + + // This method return true iff the provided |owner_password| is the current + // owner password in the Tpm. This method can also return false if there was + // an error communicating with the Tpm. + bool TestTpmAuth(const std::string& owner_password); + + OpensslCryptoUtil openssl_util_; + TpmConnection tpm_connection_; + LocalDataStore* local_data_store_; + TpmStatus* tpm_status_; + + DISALLOW_COPY_AND_ASSIGN(TpmInitializerImpl); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_TPM_INITIALIZER_IMPL_H_ diff --git a/server/tpm_manager-seccomp-amd64.policy b/server/tpm_manager-seccomp-amd64.policy index 4326566..641bce6 100644 --- a/server/tpm_manager-seccomp-amd64.policy +++ b/server/tpm_manager-seccomp-amd64.policy @@ -58,5 +58,6 @@ mprotect: 1 munmap: 1 clone: 1 -#This is attempted but apparently not necessary; return EPERM. +# These calls are attempted but apparently not necessary; return EPERM. prctl: return 1 +ioctl: return 1 diff --git a/server/tpm_manager_service.cc b/server/tpm_manager_service.cc index 57f33d9..f7077d4 100644 --- a/server/tpm_manager_service.cc +++ b/server/tpm_manager_service.cc @@ -12,9 +12,11 @@ namespace tpm_manager { TpmManagerService::TpmManagerService(bool wait_for_ownership, LocalDataStore* local_data_store, - TpmStatus* tpm_status) + TpmStatus* tpm_status, + TpmInitializer* tpm_initializer) : local_data_store_(local_data_store), tpm_status_(tpm_status), + tpm_initializer_(tpm_initializer), wait_for_ownership_(wait_for_ownership), weak_factory_(this) { } @@ -106,6 +108,7 @@ void TpmManagerService::TakeOwnershipTask( result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } + result->set_status(STATUS_SUCCESS); } } // namespace tpm_manager diff --git a/server/tpm_manager_service.h b/server/tpm_manager_service.h index 1fcb367..d715816 100644 --- a/server/tpm_manager_service.h +++ b/server/tpm_manager_service.h @@ -43,10 +43,11 @@ class TpmManagerService : public TpmManagerInterface { public: // If |wait_for_ownership| is set, TPM initialization will be postponed until // an explicit TakeOwnership request is received. Does not take ownership of - // |local_data_store| or |tpm_status|. + // |local_data_store|, |tpm_status| or |tpm_initializer|. explicit TpmManagerService(bool wait_for_ownership, LocalDataStore* local_data_store, - TpmStatus* tpm_status); + TpmStatus* tpm_status, + TpmInitializer* tpm_initializer); ~TpmManagerService() override = default; // TpmManagerInterface methods. @@ -56,11 +57,6 @@ class TpmManagerService : public TpmManagerInterface { void TakeOwnership(const TakeOwnershipRequest& request, const TakeOwnershipCallback& callback) override; - // Mutators useful for injecting dependencies for testing. - void set_tpm_initializer(TpmInitializer* initializer) { - tpm_initializer_ = initializer; - } - private: // A relay callback which allows the use of weak pointer semantics for a reply // to TaskRunner::PostTaskAndReply. @@ -88,8 +84,8 @@ class TpmManagerService : public TpmManagerInterface { void InitializeTask(); LocalDataStore* local_data_store_; - TpmInitializer* tpm_initializer_{nullptr}; TpmStatus* tpm_status_; + TpmInitializer* tpm_initializer_; // Whether to wait for an explicit call to 'TakeOwnership' before initializing // the TPM. Normally tracks the --wait_for_ownership command line option. bool wait_for_ownership_; diff --git a/server/tpm_manager_service_test.cc b/server/tpm_manager_service_test.cc index 1aafe5f..d1c36fb 100644 --- a/server/tpm_manager_service_test.cc +++ b/server/tpm_manager_service_test.cc @@ -28,7 +28,8 @@ class TpmManagerServiceTest : public testing::Test { void SetUp() override { service_.reset(new TpmManagerService(true /*wait_for_ownership*/, &mock_local_data_store_, - &mock_tpm_status_)); + &mock_tpm_status_, + &mock_tpm_initializer_)); SetupService(); } @@ -53,7 +54,6 @@ class TpmManagerServiceTest : public testing::Test { } void SetupService() { - service_->set_tpm_initializer(&mock_tpm_initializer_); CHECK(service_->Initialize()); } @@ -74,7 +74,8 @@ class TpmManagerServiceTest_NoWaitForOwnership : public TpmManagerServiceTest { void SetUp() override { service_.reset(new TpmManagerService(false /*wait_for_ownership*/, &mock_local_data_store_, - &mock_tpm_status_)); + &mock_tpm_status_, + &mock_tpm_initializer_)); } }; diff --git a/server/tpm_status_impl.cc b/server/tpm_status_impl.cc index 4948761..f390e68 100644 --- a/server/tpm_status_impl.cc +++ b/server/tpm_status_impl.cc @@ -6,7 +6,7 @@ #include <vector> -#include <base/stl_util.h> +#include <base/logging.h> #include <trousers/tss.h> #include <trousers/trousers.h> // NOLINT(build/include_alpha) diff --git a/server/tpm_util.h b/server/tpm_util.h new file mode 100644 index 0000000..3b7c93b --- /dev/null +++ b/server/tpm_util.h @@ -0,0 +1,16 @@ +// Copyright 2015 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. + +#ifndef TPM_MANAGER_SERVER_TPM_UTIL_H_ +#define TPM_MANAGER_SERVER_TPM_UTIL_H_ + +namespace tpm_manager { + +#define TPM_LOG(severity, result) \ + LOG(severity) << "TPM error 0x" << std::hex << result \ + << " (" << Trspi_Error_String(result) << "): " + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_TPM_UTIL_H_ diff --git a/tpm_manager.gyp b/tpm_manager.gyp index d0c6a8c..1f213ad 100644 --- a/tpm_manager.gyp +++ b/tpm_manager.gyp @@ -9,6 +9,7 @@ 'deps': [ # This is a list of pkg-config dependencies 'libchrome-<(libbase_ver)', 'libchromeos-<(libbase_ver)', + 'openssl', 'protobuf-lite', ], }, @@ -61,12 +62,14 @@ 'sources': [ 'server/dbus_service.cc', 'server/local_data_store_impl.cc', + 'server/openssl_crypto_util.cc', 'server/tpm_manager_service.cc', ], 'conditions': [ ['USE_tpm2 == 1', { 'sources': [ 'server/tpm2_status_impl.cc', + 'server/tpm2_initializer_impl.cc', ], 'all_dependent_settings': { 'libraries': [ @@ -78,6 +81,7 @@ 'sources': [ 'server/tpm_connection.cc', 'server/tpm_status_impl.cc', + 'server/tpm_initializer_impl.cc', ], 'all_dependent_settings': { 'libraries': [ @@ -143,6 +147,7 @@ '../trunks/mock_tpm_utility.cc', '../trunks/trunks_factory_for_test.cc', 'server/tpm2_status_test.cc', + 'server/tpm2_initializer_test.cc', ], }], ], |