diff options
author | Bill Yi <byi@google.com> | 2015-09-09 16:59:53 -0700 |
---|---|---|
committer | Bill Yi <byi@google.com> | 2015-09-09 16:59:53 -0700 |
commit | 22782fa6dfd49381b701ebaaada6612b62719f34 (patch) | |
tree | 6b49512635ddd192c10077add62efcf6f5dd9cd7 | |
parent | d9d0a2a68a7eb79cb0ab212fd5fe77c193728081 (diff) | |
parent | 50e52ff6bcc478118a1cdec27903a5af5061d77b (diff) | |
download | tpm_manager-22782fa6dfd49381b701ebaaada6612b62719f34.tar.gz |
Merge branch 'rewrite-tpm_manager' into merge-tpm_manager
55 files changed, 3527 insertions, 0 deletions
diff --git a/client/dbus_proxy.cc b/client/dbus_proxy.cc new file mode 100644 index 0000000..b5429f2 --- /dev/null +++ b/client/dbus_proxy.cc @@ -0,0 +1,73 @@ +// 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/client/dbus_proxy.h" + +#include <chromeos/bind_lambda.h> +#include <chromeos/dbus/dbus_method_invoker.h> + +#include "tpm_manager/common/dbus_interface.h" + +namespace { + +// Use a two minute timeout because TPM operations can take a long time. +const int kDBusTimeoutMS = 2 * 60 * 1000; + +} // namespace + +namespace tpm_manager { + +DBusProxy::DBusProxy() {} + +DBusProxy::~DBusProxy() { + if (bus_) { + bus_->ShutdownAndBlock(); + } +} + +bool DBusProxy::Initialize() { + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SYSTEM; + bus_ = new dbus::Bus(options); + object_proxy_ = bus_->GetObjectProxy( + tpm_manager::kTpmManagerServiceName, + dbus::ObjectPath(tpm_manager::kTpmManagerServicePath)); + return (object_proxy_ != nullptr); +} + +void DBusProxy::GetTpmStatus(const GetTpmStatusRequest& request, + const GetTpmStatusCallback& callback) { + auto on_error = [callback](chromeos::Error* error) { + GetTpmStatusReply reply; + reply.set_status(STATUS_NOT_AVAILABLE); + callback.Run(reply); + }; + chromeos::dbus_utils::CallMethodWithTimeout( + kDBusTimeoutMS, + object_proxy_, + tpm_manager::kTpmManagerInterface, + tpm_manager::kGetTpmStatus, + callback, + base::Bind(on_error), + request); +} + +void DBusProxy::TakeOwnership(const TakeOwnershipRequest& request, + const TakeOwnershipCallback& callback) { + auto on_error = [callback](chromeos::Error* error) { + TakeOwnershipReply reply; + reply.set_status(STATUS_NOT_AVAILABLE); + callback.Run(reply); + }; + chromeos::dbus_utils::CallMethodWithTimeout( + kDBusTimeoutMS, + object_proxy_, + tpm_manager::kTpmManagerInterface, + tpm_manager::kTakeOwnership, + callback, + base::Bind(on_error), + request); +} + +} // namespace tpm_manager diff --git a/client/dbus_proxy.h b/client/dbus_proxy.h new file mode 100644 index 0000000..b99b735 --- /dev/null +++ b/client/dbus_proxy.h @@ -0,0 +1,49 @@ +// 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_CLIENT_DBUS_PROXY_H_ +#define TPM_MANAGER_CLIENT_DBUS_PROXY_H_ + +#include "tpm_manager/common/tpm_manager_interface.h" + +#include <string> + +#include <base/memory/ref_counted.h> +#include <dbus/bus.h> +#include <dbus/object_proxy.h> + +#include "tpm_manager/common/export.h" + +namespace tpm_manager { + +// An implementation of TpmManagerInterface that forwards requests to +// tpm_managerd over D-Bus. +// Usage: +// std::unique_ptr<TpmManagerInterface> tpm_manager = new DBusProxy(); +// tpm_manager->GetTpmStatus(...); +class TPM_MANAGER_EXPORT DBusProxy : public TpmManagerInterface { + public: + DBusProxy(); + virtual ~DBusProxy(); + + // TpmManagerInterface methods. + bool Initialize() override; + void GetTpmStatus(const GetTpmStatusRequest& request, + const GetTpmStatusCallback& callback) override; + void TakeOwnership(const TakeOwnershipRequest& request, + const TakeOwnershipCallback& callback) override; + + void set_object_proxy(dbus::ObjectProxy* object_proxy) { + object_proxy_ = object_proxy; + } + + private: + scoped_refptr<dbus::Bus> bus_; + dbus::ObjectProxy* object_proxy_; + DISALLOW_COPY_AND_ASSIGN(DBusProxy); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_CLIENT_DBUS_PROXY_H_ diff --git a/client/dbus_proxy_test.cc b/client/dbus_proxy_test.cc new file mode 100644 index 0000000..121896d --- /dev/null +++ b/client/dbus_proxy_test.cc @@ -0,0 +1,108 @@ +// 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 <string> + +#include <chromeos/bind_lambda.h> +#include <dbus/mock_object_proxy.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "tpm_manager/client/dbus_proxy.h" + +using testing::_; +using testing::Invoke; +using testing::StrictMock; +using testing::WithArgs; + +namespace tpm_manager { + +class DBusProxyTest : public testing::Test { + public: + ~DBusProxyTest() override = default; + void SetUp() override { + mock_object_proxy_ = new StrictMock<dbus::MockObjectProxy>( + nullptr, "", dbus::ObjectPath("")); + proxy_.set_object_proxy(mock_object_proxy_.get()); + } + protected: + scoped_refptr<StrictMock<dbus::MockObjectProxy>> mock_object_proxy_; + DBusProxy proxy_; +}; + +TEST_F(DBusProxyTest, GetTpmStatus) { + auto fake_dbus_call = []( + dbus::MethodCall* method_call, + const dbus::MockObjectProxy::ResponseCallback& response_callback) { + // Verify request protobuf. + dbus::MessageReader reader(method_call); + GetTpmStatusRequest request; + EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request)); + // Create reply protobuf. + auto response = dbus::Response::CreateEmpty(); + dbus::MessageWriter writer(response.get()); + GetTpmStatusReply reply; + reply.set_status(STATUS_NOT_AVAILABLE); + reply.set_enabled(true); + reply.set_owned(true); + reply.mutable_local_data()->set_owned_by_this_install(true); + reply.set_dictionary_attack_counter(3); + reply.set_dictionary_attack_threshold(4); + reply.set_dictionary_attack_lockout_in_effect(true); + reply.set_dictionary_attack_lockout_seconds_remaining(5); + writer.AppendProtoAsArrayOfBytes(reply); + response_callback.Run(response.release()); + }; + EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _)) + .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call))); + + // Set expectations on the outputs. + int callback_count = 0; + auto callback = [&callback_count](const GetTpmStatusReply& reply) { + callback_count++; + EXPECT_EQ(STATUS_NOT_AVAILABLE, reply.status()); + EXPECT_TRUE(reply.enabled()); + EXPECT_TRUE(reply.owned()); + EXPECT_TRUE(reply.local_data().owned_by_this_install()); + EXPECT_EQ(3, reply.dictionary_attack_counter()); + EXPECT_EQ(4, reply.dictionary_attack_threshold()); + EXPECT_TRUE(reply.dictionary_attack_lockout_in_effect()); + EXPECT_EQ(5, reply.dictionary_attack_lockout_seconds_remaining()); + }; + GetTpmStatusRequest request; + proxy_.GetTpmStatus(request, base::Bind(callback)); + EXPECT_EQ(1, callback_count); +} + +TEST_F(DBusProxyTest, TakeOwnership) { + auto fake_dbus_call = []( + dbus::MethodCall* method_call, + const dbus::MockObjectProxy::ResponseCallback& response_callback) { + // Verify request protobuf. + dbus::MessageReader reader(method_call); + TakeOwnershipRequest request; + EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request)); + // Create reply protobuf. + auto response = dbus::Response::CreateEmpty(); + dbus::MessageWriter writer(response.get()); + TakeOwnershipReply reply; + reply.set_status(STATUS_NOT_AVAILABLE); + writer.AppendProtoAsArrayOfBytes(reply); + response_callback.Run(response.release()); + }; + EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _)) + .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call))); + + // Set expectations on the outputs. + int callback_count = 0; + auto callback = [&callback_count](const TakeOwnershipReply& reply) { + callback_count++; + EXPECT_EQ(STATUS_NOT_AVAILABLE, reply.status()); + }; + TakeOwnershipRequest request; + proxy_.TakeOwnership(request, base::Bind(callback)); + EXPECT_EQ(1, callback_count); +} + +} // namespace tpm_manager diff --git a/client/main.cc b/client/main.cc new file mode 100644 index 0000000..376e0aa --- /dev/null +++ b/client/main.cc @@ -0,0 +1,136 @@ +// 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 <stdio.h> +#include <sysexits.h> + +#include <memory> +#include <string> + +#include <base/command_line.h> +#include <base/logging.h> +#include <base/message_loop/message_loop.h> +#include <chromeos/bind_lambda.h> +#include <chromeos/daemons/daemon.h> +#include <chromeos/syslog_logging.h> + +#include "tpm_manager/client/dbus_proxy.h" +#include "tpm_manager/common/dbus_interface.pb.h" +#include "tpm_manager/common/print_dbus_interface_proto.h" + +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; +class ClientLoop : public ClientLoopBase { + public: + ClientLoop() = default; + ~ClientLoop() override = default; + + protected: + int OnInit() override { + int exit_code = ClientLoopBase::OnInit(); + if (exit_code != EX_OK) { + LOG(ERROR) << "Error initializing tpm_manager_client."; + return exit_code; + } + tpm_manager_.reset(new tpm_manager::DBusProxy()); + if (!tpm_manager_->Initialize()) { + LOG(ERROR) << "Error initializing dbus proxy to tpm_managerd."; + return EX_UNAVAILABLE; + } + exit_code = ScheduleCommand(); + if (exit_code == EX_USAGE) { + printf("%s", kUsage); + } + return exit_code; + } + + void OnShutdown(int* exit_code) override { + tpm_manager_.reset(); + ClientLoopBase::OnShutdown(exit_code); + } + + private: + // Posts tasks on to the message loop based on command line flags. + int ScheduleCommand() { + base::Closure task; + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch("help") || command_line->HasSwitch("h")) { + return EX_USAGE; + } 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."; + return EX_USAGE; + } + base::MessageLoop::current()->PostTask(FROM_HERE, task); + return EX_OK; + } + + void PrintGetTpmStatusReply(const GetTpmStatusReply& reply) { + if (reply.has_status() && reply.status() == STATUS_NOT_AVAILABLE) { + LOG(INFO) << "tpm_managerd is not available."; + } else { + LOG(INFO) << "TpmStatusReply: " << GetProtoDebugString(reply); + } + Quit(); + } + + void HandleGetTpmStatus() { + GetTpmStatusRequest request; + tpm_manager_->GetTpmStatus( + request, + base::Bind(&ClientLoop::PrintGetTpmStatusReply, + 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_; + + // Declared last so that weak pointers will be destroyed first. + base::WeakPtrFactory<ClientLoop> weak_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(ClientLoop); +}; + +} // namespace tpm_manager + +int main(int argc, char* argv[]) { + base::CommandLine::Init(argc, argv); + chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogToStderr); + tpm_manager::ClientLoop loop; + return loop.Run(); +} diff --git a/common/dbus_interface.h b/common/dbus_interface.h new file mode 100644 index 0000000..d0f8981 --- /dev/null +++ b/common/dbus_interface.h @@ -0,0 +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. + +#ifndef TPM_MANAGER_COMMON_DBUS_INTERFACE_H_ +#define TPM_MANAGER_COMMON_DBUS_INTERFACE_H_ + +namespace tpm_manager { + +constexpr char kTpmManagerInterface[] = "org.chromium.TpmManager"; +constexpr char kTpmManagerServicePath[] = "/org/chromium/TpmManager"; +constexpr char kTpmManagerServiceName[] = "org.chromium.TpmManager"; + +// Methods exported by tpm_manager. +constexpr char kGetTpmStatus[] = "GetTpmStatus"; +constexpr char kTakeOwnership[] = "TakeOwnership"; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_COMMON_DBUS_INTERFACE_H_ diff --git a/common/dbus_interface.proto b/common/dbus_interface.proto new file mode 100644 index 0000000..216bda5 --- /dev/null +++ b/common/dbus_interface.proto @@ -0,0 +1,44 @@ +option optimize_for = LITE_RUNTIME; +package tpm_manager; + +import "local_data.proto"; + +// The messages in this file correspond to the TpmManager D-Bus interface. + +enum TpmManagerStatus { + STATUS_SUCCESS = 0; + STATUS_UNEXPECTED_DEVICE_ERROR = 1; + STATUS_NOT_AVAILABLE = 2; +} + +// Input for the GetTpmStatus method. +message GetTpmStatusRequest { +} + +// Output from the GetTpmStatus method. +message GetTpmStatusReply { + optional TpmManagerStatus status = 1; + // Whether a TPM is enabled on the system. + optional bool enabled = 2; + // Whether the TPM has been owned. + optional bool owned = 3; + // Local TPM management data (including the owner password if available). + optional LocalData local_data = 4; + // The current dictionary attack counter value. + optional int32 dictionary_attack_counter = 5; + // The current dictionary attack counter threshold. + optional int32 dictionary_attack_threshold = 6; + // Whether the TPM is in some form of dictionary attack lockout. + optional bool dictionary_attack_lockout_in_effect = 7; + // The number of seconds remaining in the lockout. + optional int32 dictionary_attack_lockout_seconds_remaining = 8; +} + +// Input for the TakeOwnership method. +message TakeOwnershipRequest { +} + +// Output from the TakeOwnership method. +message TakeOwnershipReply { + optional TpmManagerStatus status = 1; +} diff --git a/common/export.h b/common/export.h new file mode 100644 index 0000000..6973236 --- /dev/null +++ b/common/export.h @@ -0,0 +1,12 @@ +// 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_COMMON_EXPORT_H_ +#define TPM_MANAGER_COMMON_EXPORT_H_ + +// Use this for any class or function that needs to be exported from +// libtpm_manager. E.g. TPM_MANAGER_EXPORT void foo(); +#define TPM_MANAGER_EXPORT __attribute__((__visibility__("default"))) + +#endif // TPM_MANAGER_COMMON_EXPORT_H_ diff --git a/common/local_data.proto b/common/local_data.proto new file mode 100644 index 0000000..509f027 --- /dev/null +++ b/common/local_data.proto @@ -0,0 +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/common/mock_tpm_manager_interface.cc b/common/mock_tpm_manager_interface.cc new file mode 100644 index 0000000..f16a1b5 --- /dev/null +++ b/common/mock_tpm_manager_interface.cc @@ -0,0 +1,12 @@ +// 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/common/mock_tpm_manager_interface.h" + +namespace tpm_manager { + +MockTpmManagerInterface::MockTpmManagerInterface() {} +MockTpmManagerInterface::~MockTpmManagerInterface() {} + +} // namespace tpm_manager diff --git a/common/mock_tpm_manager_interface.h b/common/mock_tpm_manager_interface.h new file mode 100644 index 0000000..664f91c --- /dev/null +++ b/common/mock_tpm_manager_interface.h @@ -0,0 +1,30 @@ +// 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_COMMON_MOCK_TPM_MANAGER_INTERFACE_H_ +#define TPM_MANAGER_COMMON_MOCK_TPM_MANAGER_INTERFACE_H_ + +#include "tpm_manager/common/tpm_manager_interface.h" + +#include <gmock/gmock.h> + +namespace tpm_manager { + +class MockTpmManagerInterface : public TpmManagerInterface { + public: + MockTpmManagerInterface(); + ~MockTpmManagerInterface() override; + + MOCK_METHOD0(Initialize, bool()); + MOCK_METHOD2(GetTpmStatus, + void(const GetTpmStatusRequest& request, + const GetTpmStatusCallback& callback)); + MOCK_METHOD2(TakeOwnership, + void(const TakeOwnershipRequest& request, + const TakeOwnershipCallback& callback)); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_COMMON_MOCK_TPM_MANAGER_INTERFACE_H_ diff --git a/common/print_dbus_interface_proto.cc b/common/print_dbus_interface_proto.cc new file mode 100644 index 0000000..0fb1fd1 --- /dev/null +++ b/common/print_dbus_interface_proto.cc @@ -0,0 +1,146 @@ +// 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. + +// THIS CODE IS GENERATED. + +#include "tpm_manager/common/print_dbus_interface_proto.h" + +#include <string> + +#include <base/strings/string_number_conversions.h> +#include <base/strings/stringprintf.h> + +#include "tpm_manager/common/print_local_data_proto.h" + +namespace tpm_manager { + +std::string GetProtoDebugString(TpmManagerStatus value) { + return GetProtoDebugStringWithIndent(value, 0); +} + +std::string GetProtoDebugStringWithIndent(TpmManagerStatus value, + int indent_size) { + if (value == STATUS_SUCCESS) { + return "STATUS_SUCCESS"; + } + if (value == STATUS_UNEXPECTED_DEVICE_ERROR) { + return "STATUS_UNEXPECTED_DEVICE_ERROR"; + } + if (value == STATUS_NOT_AVAILABLE) { + return "STATUS_NOT_AVAILABLE"; + } + return "<unknown>"; +} + +std::string GetProtoDebugString(const GetTpmStatusRequest& value) { + return GetProtoDebugStringWithIndent(value, 0); +} + +std::string GetProtoDebugStringWithIndent(const GetTpmStatusRequest& value, + int indent_size) { + std::string indent(indent_size, ' '); + std::string output = + base::StringPrintf("[%s] {\n", value.GetTypeName().c_str()); + + output += indent + "}\n"; + return output; +} + +std::string GetProtoDebugString(const GetTpmStatusReply& value) { + return GetProtoDebugStringWithIndent(value, 0); +} + +std::string GetProtoDebugStringWithIndent(const GetTpmStatusReply& value, + int indent_size) { + std::string indent(indent_size, ' '); + std::string output = + base::StringPrintf("[%s] {\n", value.GetTypeName().c_str()); + + if (value.has_status()) { + output += indent + " status: "; + base::StringAppendF( + &output, "%s", + GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str()); + output += "\n"; + } + if (value.has_enabled()) { + output += indent + " enabled: "; + base::StringAppendF(&output, "%s", value.enabled() ? "true" : "false"); + output += "\n"; + } + if (value.has_owned()) { + output += indent + " owned: "; + base::StringAppendF(&output, "%s", value.owned() ? "true" : "false"); + output += "\n"; + } + if (value.has_local_data()) { + output += indent + " local_data: "; + base::StringAppendF(&output, "%s", GetProtoDebugStringWithIndent( + value.local_data(), indent_size + 2) + .c_str()); + output += "\n"; + } + if (value.has_dictionary_attack_counter()) { + output += indent + " dictionary_attack_counter: "; + base::StringAppendF(&output, "%d", value.dictionary_attack_counter()); + output += "\n"; + } + if (value.has_dictionary_attack_threshold()) { + output += indent + " dictionary_attack_threshold: "; + base::StringAppendF(&output, "%d", value.dictionary_attack_threshold()); + output += "\n"; + } + if (value.has_dictionary_attack_lockout_in_effect()) { + output += indent + " dictionary_attack_lockout_in_effect: "; + base::StringAppendF( + &output, "%s", + value.dictionary_attack_lockout_in_effect() ? "true" : "false"); + output += "\n"; + } + if (value.has_dictionary_attack_lockout_seconds_remaining()) { + output += indent + " dictionary_attack_lockout_seconds_remaining: "; + base::StringAppendF(&output, "%d", + value.dictionary_attack_lockout_seconds_remaining()); + output += "\n"; + } + output += indent + "}\n"; + return output; +} + +std::string GetProtoDebugString(const TakeOwnershipRequest& value) { + return GetProtoDebugStringWithIndent(value, 0); +} + +std::string GetProtoDebugStringWithIndent(const TakeOwnershipRequest& value, + int indent_size) { + std::string indent(indent_size, ' '); + std::string output = + base::StringPrintf("[%s] {\n", value.GetTypeName().c_str()); + + output += indent + "}\n"; + return output; +} + +std::string GetProtoDebugString(const TakeOwnershipReply& value) { + return GetProtoDebugStringWithIndent(value, 0); +} + +std::string GetProtoDebugStringWithIndent(const TakeOwnershipReply& value, + int indent_size) { + std::string indent(indent_size, ' '); + std::string output = + base::StringPrintf("[%s] {\n", value.GetTypeName().c_str()); + + if (value.has_status()) { + output += indent + " status: "; + base::StringAppendF( + &output, "%s", + GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str()); + output += "\n"; + } + output += indent + "}\n"; + return output; +} + +} // namespace tpm_manager diff --git a/common/print_dbus_interface_proto.h b/common/print_dbus_interface_proto.h new file mode 100644 index 0000000..d0e01d2 --- /dev/null +++ b/common/print_dbus_interface_proto.h @@ -0,0 +1,34 @@ +// 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. + +// THIS CODE IS GENERATED. + +#ifndef TPM_MANAGER_COMMON_PRINT_DBUS_INTERFACE_PROTO_H_ +#define TPM_MANAGER_COMMON_PRINT_DBUS_INTERFACE_PROTO_H_ + +#include <string> + +#include "tpm_manager/common/dbus_interface.pb.h" + +namespace tpm_manager { + +std::string GetProtoDebugStringWithIndent(TpmManagerStatus value, + int indent_size); +std::string GetProtoDebugString(TpmManagerStatus value); +std::string GetProtoDebugStringWithIndent(const GetTpmStatusRequest& value, + int indent_size); +std::string GetProtoDebugString(const GetTpmStatusRequest& value); +std::string GetProtoDebugStringWithIndent(const GetTpmStatusReply& value, + int indent_size); +std::string GetProtoDebugString(const GetTpmStatusReply& value); +std::string GetProtoDebugStringWithIndent(const TakeOwnershipRequest& value, + int indent_size); +std::string GetProtoDebugString(const TakeOwnershipRequest& value); +std::string GetProtoDebugStringWithIndent(const TakeOwnershipReply& value, + int indent_size); +std::string GetProtoDebugString(const TakeOwnershipReply& value); + +} // namespace tpm_manager + +#endif // TPM_MANAGER_COMMON_PRINT_DBUS_INTERFACE_PROTO_H_ diff --git a/common/print_local_data_proto.cc b/common/print_local_data_proto.cc new file mode 100644 index 0000000..c25dcaf --- /dev/null +++ b/common/print_local_data_proto.cc @@ -0,0 +1,49 @@ +// 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. + +// THIS CODE IS GENERATED. + +#include "tpm_manager/common/print_local_data_proto.h" + +#include <string> + +#include <base/strings/string_number_conversions.h> +#include <base/strings/stringprintf.h> + +namespace tpm_manager { + +std::string GetProtoDebugString(const LocalData& value) { + return GetProtoDebugStringWithIndent(value, 0); +} + +std::string GetProtoDebugStringWithIndent(const LocalData& value, + int indent_size) { + std::string indent(indent_size, ' '); + std::string output = + base::StringPrintf("[%s] {\n", value.GetTypeName().c_str()); + + if (value.has_owned_by_this_install()) { + output += indent + " owned_by_this_install: "; + base::StringAppendF(&output, "%s", + value.owned_by_this_install() ? "true" : "false"); + output += "\n"; + } + if (value.has_owner_password()) { + output += indent + " owner_password: "; + base::StringAppendF(&output, "%s", + base::HexEncode(value.owner_password().data(), + value.owner_password().size()) + .c_str()); + output += "\n"; + } + output += indent + " owner_dependency: {"; + for (int i = 0; i < value.owner_dependency_size(); ++i) { + base::StringAppendF(&output, "%s", value.owner_dependency(i).c_str()); + } + output += "}\n"; + output += indent + "}\n"; + return output; +} + +} // namespace tpm_manager diff --git a/common/print_local_data_proto.h b/common/print_local_data_proto.h new file mode 100644 index 0000000..2114a32 --- /dev/null +++ b/common/print_local_data_proto.h @@ -0,0 +1,22 @@ +// 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. + +// THIS CODE IS GENERATED. + +#ifndef TPM_MANAGER_COMMON_PRINT_LOCAL_DATA_PROTO_H_ +#define TPM_MANAGER_COMMON_PRINT_LOCAL_DATA_PROTO_H_ + +#include <string> + +#include "tpm_manager/common/local_data.pb.h" + +namespace tpm_manager { + +std::string GetProtoDebugStringWithIndent(const LocalData& value, + int indent_size); +std::string GetProtoDebugString(const LocalData& value); + +} // namespace tpm_manager + +#endif // TPM_MANAGER_COMMON_PRINT_LOCAL_DATA_PROTO_H_ diff --git a/common/tpm_manager_interface.h b/common/tpm_manager_interface.h new file mode 100644 index 0000000..bee631f --- /dev/null +++ b/common/tpm_manager_interface.h @@ -0,0 +1,38 @@ +// 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_COMMON_TPM_MANAGER_INTERFACE_H_ +#define TPM_MANAGER_COMMON_TPM_MANAGER_INTERFACE_H_ + +#include <base/callback.h> + +#include "tpm_manager/common/dbus_interface.pb.h" +#include "tpm_manager/common/export.h" + +namespace tpm_manager { + +// This is the main TpmManager interface that is implemented by the proxies +// and services. +class TPM_MANAGER_EXPORT TpmManagerInterface { + public: + virtual ~TpmManagerInterface() = default; + + // Performs initialization tasks. This method must be called before calling + // any other method on this interface. + virtual bool Initialize() = 0; + + // Processes a GetTpmStatusRequest and responds with a GetTpmStatusReply. + using GetTpmStatusCallback = base::Callback<void(const GetTpmStatusReply&)>; + virtual void GetTpmStatus(const GetTpmStatusRequest& request, + const GetTpmStatusCallback& callback) = 0; + + // Processes a TakeOwnershipRequest and responds with a TakeOwnershipReply. + using TakeOwnershipCallback = base::Callback<void(const TakeOwnershipReply&)>; + virtual void TakeOwnership(const TakeOwnershipRequest& request, + const TakeOwnershipCallback& callback) = 0; +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_COMMON_TPM_MANAGER_INTERFACE_H_ diff --git a/server/dbus_service.cc b/server/dbus_service.cc new file mode 100644 index 0000000..e326f0c --- /dev/null +++ b/server/dbus_service.cc @@ -0,0 +1,70 @@ +// 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/dbus_service.h" + +#include <memory> +#include <string> + +#include <chromeos/bind_lambda.h> +#include <dbus/bus.h> +#include <dbus/object_path.h> + +#include "tpm_manager/common/dbus_interface.h" + +using chromeos::dbus_utils::DBusMethodResponse; + +namespace tpm_manager { + +DBusService::DBusService(const scoped_refptr<dbus::Bus>& bus, + TpmManagerInterface* service) + : dbus_object_(nullptr, bus, dbus::ObjectPath(kTpmManagerServicePath)), + service_(service) {} + +void DBusService::Register(const CompletionAction& callback) { + chromeos::dbus_utils::DBusInterface* dbus_interface = + dbus_object_.AddOrGetInterface(kTpmManagerInterface); + + dbus_interface->AddMethodHandler(kGetTpmStatus, base::Unretained(this), + &DBusService::HandleGetTpmStatus); + dbus_interface->AddMethodHandler(kTakeOwnership, base::Unretained(this), + &DBusService::HandleTakeOwnership); + dbus_object_.RegisterAsync(callback); +} + +void DBusService::HandleGetTpmStatus( + std::unique_ptr<DBusMethodResponse<const GetTpmStatusReply&>> response, + const GetTpmStatusRequest& request) { + // Convert |response| to a shared_ptr so |service_| can safely copy the + // callback. + using SharedResponsePointer = std::shared_ptr< + DBusMethodResponse<const GetTpmStatusReply&>>; + // A callback that sends off the reply protobuf. + auto callback = [](const SharedResponsePointer& response, + const GetTpmStatusReply& reply) { + response->Return(reply); + }; + service_->GetTpmStatus( + request, + base::Bind(callback, SharedResponsePointer(std::move(response)))); +} + +void DBusService::HandleTakeOwnership( + std::unique_ptr<DBusMethodResponse<const TakeOwnershipReply&>> response, + const TakeOwnershipRequest& request) { + // Convert |response| to a shared_ptr so |service_| can safely copy the + // callback. + using SharedResponsePointer = std::shared_ptr< + DBusMethodResponse<const TakeOwnershipReply&>>; + // A callback that sends off the reply protobuf. + auto callback = [](const SharedResponsePointer& response, + const TakeOwnershipReply& reply) { + response->Return(reply); + }; + service_->TakeOwnership( + request, + base::Bind(callback, SharedResponsePointer(std::move(response)))); +} + +} // namespace tpm_manager diff --git a/server/dbus_service.h b/server/dbus_service.h new file mode 100644 index 0000000..8286ced --- /dev/null +++ b/server/dbus_service.h @@ -0,0 +1,59 @@ +// 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_DBUS_SERVICE_H_ +#define TPM_MANAGER_SERVER_DBUS_SERVICE_H_ + +#include <memory> + +#include <chromeos/dbus/dbus_method_response.h> +#include <chromeos/dbus/dbus_object.h> +#include <dbus/bus.h> + +#include "tpm_manager/common/tpm_manager_interface.h" + +namespace tpm_manager { + +using CompletionAction = + chromeos::dbus_utils::AsyncEventSequencer::CompletionAction; + +// Handles D-Bus communtion with the TpmManager daemon. +class DBusService { + public: + // Does not take ownership of |service|. |service| must remain valid for the + // lifetime of this instance. + DBusService(const scoped_refptr<dbus::Bus>& bus, + TpmManagerInterface* service); + virtual ~DBusService() = default; + + // Connects to D-Bus system bus and exports TpmManager methods. + void Register(const CompletionAction& callback); + + void set_service(TpmManagerInterface* service) { + service_ = service; + } + + private: + friend class DBusServiceTest; + + // Handles the GetTpmStatus D-Bus call. + void HandleGetTpmStatus( + std::unique_ptr<chromeos::dbus_utils::DBusMethodResponse< + const GetTpmStatusReply&>> response, + const GetTpmStatusRequest& request); + + // Handles the TakeOwnership D-Bus call. + void HandleTakeOwnership( + std::unique_ptr<chromeos::dbus_utils::DBusMethodResponse< + const TakeOwnershipReply&>> response, + const TakeOwnershipRequest& request); + + chromeos::dbus_utils::DBusObject dbus_object_; + TpmManagerInterface* service_; + DISALLOW_COPY_AND_ASSIGN(DBusService); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_DBUS_SERVICE_H_ diff --git a/server/dbus_service_test.cc b/server/dbus_service_test.cc new file mode 100644 index 0000000..0647951 --- /dev/null +++ b/server/dbus_service_test.cc @@ -0,0 +1,136 @@ +// 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 <string> + +#include <chromeos/bind_lambda.h> +#include <chromeos/dbus/dbus_object_test_helpers.h> +#include <dbus/mock_bus.h> +#include <dbus/mock_exported_object.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "tpm_manager/common/dbus_interface.h" +#include "tpm_manager/common/mock_tpm_manager_interface.h" +#include "tpm_manager/server/dbus_service.h" + +using testing::_; +using testing::Invoke; +using testing::NiceMock; +using testing::Return; +using testing::StrictMock; +using testing::WithArgs; + +namespace tpm_manager { + +class DBusServiceTest : public testing::Test { + public: + ~DBusServiceTest() override = default; + void SetUp() override { + dbus::Bus::Options options; + mock_bus_ = new NiceMock<dbus::MockBus>(options); + dbus::ObjectPath path(kTpmManagerServicePath); + mock_exported_object_ = new NiceMock<dbus::MockExportedObject>( + mock_bus_.get(), path); + ON_CALL(*mock_bus_, GetExportedObject(path)) + .WillByDefault(Return(mock_exported_object_.get())); + dbus_service_.reset(new DBusService(mock_bus_, &mock_service_)); + dbus_service_->Register(chromeos::dbus_utils::AsyncEventSequencer:: + GetDefaultCompletionAction()); + } + + std::unique_ptr<dbus::Response> CallMethod(dbus::MethodCall* method_call) { + return chromeos::dbus_utils::testing::CallMethod( + dbus_service_->dbus_object_, method_call); + } + + std::unique_ptr<dbus::MethodCall> CreateMethodCall( + const std::string& method_name) { + std::unique_ptr<dbus::MethodCall> call(new dbus::MethodCall( + kTpmManagerInterface, method_name)); + call->SetSerial(1); + return call; + } + + protected: + scoped_refptr<dbus::MockBus> mock_bus_; + scoped_refptr<dbus::MockExportedObject> mock_exported_object_; + StrictMock<MockTpmManagerInterface> mock_service_; + std::unique_ptr<DBusService> dbus_service_; +}; + +TEST_F(DBusServiceTest, CopyableCallback) { + EXPECT_CALL(mock_service_, GetTpmStatus(_, _)) + .WillOnce(WithArgs<1>( + Invoke([](const TpmManagerInterface::GetTpmStatusCallback& callback) { + // Copy the callback, then call the original. + GetTpmStatusReply reply; + base::Closure copy = base::Bind(callback, reply); + callback.Run(reply); + }))); + std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(kGetTpmStatus); + GetTpmStatusRequest request; + dbus::MessageWriter writer(call.get()); + writer.AppendProtoAsArrayOfBytes(request); + auto response = CallMethod(call.get()); + dbus::MessageReader reader(response.get()); + GetTpmStatusReply reply; + EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply)); +} + +TEST_F(DBusServiceTest, GetTpmStatus) { + GetTpmStatusRequest request; + EXPECT_CALL(mock_service_, GetTpmStatus(_, _)) + .WillOnce(Invoke([]( + const GetTpmStatusRequest& request, + const TpmManagerInterface::GetTpmStatusCallback& callback) { + GetTpmStatusReply reply; + reply.set_status(STATUS_NOT_AVAILABLE); + reply.set_enabled(true); + reply.set_owned(true); + reply.mutable_local_data()->set_owned_by_this_install(true); + reply.set_dictionary_attack_counter(3); + reply.set_dictionary_attack_threshold(4); + reply.set_dictionary_attack_lockout_in_effect(true); + reply.set_dictionary_attack_lockout_seconds_remaining(5); + callback.Run(reply); + })); + std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(kGetTpmStatus); + dbus::MessageWriter writer(call.get()); + writer.AppendProtoAsArrayOfBytes(request); + auto response = CallMethod(call.get()); + dbus::MessageReader reader(response.get()); + GetTpmStatusReply reply; + EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply)); + EXPECT_EQ(STATUS_NOT_AVAILABLE, reply.status()); + EXPECT_TRUE(reply.enabled()); + EXPECT_TRUE(reply.owned()); + EXPECT_TRUE(reply.local_data().owned_by_this_install()); + EXPECT_EQ(3, reply.dictionary_attack_counter()); + EXPECT_EQ(4, reply.dictionary_attack_threshold()); + EXPECT_TRUE(reply.dictionary_attack_lockout_in_effect()); + EXPECT_EQ(5, reply.dictionary_attack_lockout_seconds_remaining()); +} + +TEST_F(DBusServiceTest, TakeOwnership) { + TakeOwnershipRequest request; + EXPECT_CALL(mock_service_, TakeOwnership(_, _)) + .WillOnce(Invoke([]( + const TakeOwnershipRequest& request, + const TpmManagerInterface::TakeOwnershipCallback& callback) { + TakeOwnershipReply reply; + reply.set_status(STATUS_NOT_AVAILABLE); + callback.Run(reply); + })); + std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(kTakeOwnership); + dbus::MessageWriter writer(call.get()); + writer.AppendProtoAsArrayOfBytes(request); + auto response = CallMethod(call.get()); + dbus::MessageReader reader(response.get()); + TakeOwnershipReply reply; + EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply)); + EXPECT_EQ(STATUS_NOT_AVAILABLE, reply.status()); +} + +} // namespace tpm_manager diff --git a/server/local_data_store.h b/server/local_data_store.h new file mode 100644 index 0000000..0bfecf5 --- /dev/null +++ b/server/local_data_store.h @@ -0,0 +1,30 @@ +// 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_LOCAL_DATA_STORE_H_ +#define TPM_MANAGER_SERVER_LOCAL_DATA_STORE_H_ + +#include "tpm_manager/common/local_data.pb.h" + +namespace tpm_manager { + +// LocalDataStore is an interface class that provides access to read and write +// local system data. +class LocalDataStore { + public: + LocalDataStore() = default; + virtual ~LocalDataStore() = default; + + // Reads local |data| from persistent storage. If no local data exists, the + // output is an empty protobuf and the method succeeds. Returns true on + // success. + virtual bool Read(LocalData* data) = 0; + + // Writes local |data| to persistent storage. Returns true on success. + virtual bool Write(const LocalData& data) = 0; +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_LOCAL_DATA_STORE_H_ diff --git a/server/local_data_store_impl.cc b/server/local_data_store_impl.cc new file mode 100644 index 0000000..a3da4d7 --- /dev/null +++ b/server/local_data_store_impl.cc @@ -0,0 +1,86 @@ +// 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/local_data_store_impl.h" + +#include <fcntl.h> + +#include <string> + +#include <base/files/file_path.h> +#include <base/files/file_util.h> +#include <base/files/important_file_writer.h> + +using base::FilePath; + +namespace tpm_manager { + +const char kTpmLocalDataFile[] = + "/mnt/stateful_partition/unencrypted/preserve/local_tpm_data"; +const mode_t kLocalDataPermissions = 0600; + +bool LocalDataStoreImpl::Read(LocalData* data) { + CHECK(data); + const int kMask = base::FILE_PERMISSION_OTHERS_MASK; + FilePath path(kTpmLocalDataFile); + int permissions = 0; + if (base::GetPosixFilePermissions(path, &permissions) && + (permissions & kMask) != 0) { + base::SetPosixFilePermissions(path, permissions & ~kMask); + } + std::string file_data; + if (!ReadFileToString(path, &file_data)) { + LOG(ERROR) << "Error reading data store file."; + return false; + } + if (!data->ParseFromString(file_data)) { + LOG(ERROR) << "Error parsing file data into protobuf."; + return false; + } + return true; +} + +bool LocalDataStoreImpl::Write(const LocalData& data) { + std::string file_data; + if (!data.SerializeToString(&file_data)) { + LOG(ERROR) << "Error serializing file to string."; + return false; + } + FilePath path(kTpmLocalDataFile); + if (!base::CreateDirectory(path.DirName())) { + LOG(ERROR) << "Cannot create directory: " << path.DirName().value(); + return false; + } + if (!base::ImportantFileWriter::WriteFileAtomically(path, file_data)) { + LOG(ERROR) << "Failed to write file: " << path.value(); + return false; + } + if (!base::SetPosixFilePermissions(path, kLocalDataPermissions)) { + LOG(ERROR) << "Failed to set permissions for file: " << path.value(); + return false; + } + // Sync the parent directory. + std::string dir_name = path.DirName().value(); + int dir_fd = HANDLE_EINTR(open(dir_name.c_str(), O_RDONLY|O_DIRECTORY)); + if (dir_fd < 0) { + PLOG(WARNING) << "Could not open " << dir_name << " for syncing"; + return false; + } + // POSIX specifies EINTR as a possible return value of fsync(). + int result = HANDLE_EINTR(fsync(dir_fd)); + if (result < 0) { + PLOG(WARNING) << "Failed to sync " << dir_name; + close(dir_fd); + return false; + } + // close() may not be retried on error. + result = IGNORE_EINTR(close(dir_fd)); + if (result < 0) { + PLOG(WARNING) << "Failed to close after sync " << dir_name; + return false; + } + return true; +} + +} // namespace tpm_manager diff --git a/server/local_data_store_impl.h b/server/local_data_store_impl.h new file mode 100644 index 0000000..cb2d897 --- /dev/null +++ b/server/local_data_store_impl.h @@ -0,0 +1,33 @@ +// 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_LOCAL_DATA_STORE_IMPL_H_ +#define TPM_MANAGER_SERVER_LOCAL_DATA_STORE_IMPL_H_ + +#include "tpm_manager/server/local_data_store.h" + +#include <string> + +#include <base/macros.h> + +namespace tpm_manager { + +class LocalDataStoreImpl : public LocalDataStore { + public: + LocalDataStoreImpl() = default; + ~LocalDataStoreImpl() override = default; + + // LocalDataStore methods. + bool Read(LocalData* data) override; + bool Write(const LocalData& data) override; + + private: + DISALLOW_COPY_AND_ASSIGN(LocalDataStoreImpl); +}; + + +} // namespace tpm_manager + + +#endif // TPM_MANAGER_SERVER_LOCAL_DATA_STORE_IMPL_H_ diff --git a/server/main.cc b/server/main.cc new file mode 100644 index 0000000..f7dcc45 --- /dev/null +++ b/server/main.cc @@ -0,0 +1,93 @@ +// 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 <sysexits.h> +#include <string> + +#include <base/command_line.h> +#include <chromeos/daemons/dbus_daemon.h> +#include <chromeos/dbus/async_event_sequencer.h> +#include <chromeos/minijail/minijail.h> +#include <chromeos/syslog_logging.h> +#include <chromeos/userdb_utils.h> + +#include "tpm_manager/common/dbus_interface.h" +#include "tpm_manager/server/dbus_service.h" +#include "tpm_manager/server/local_data_store_impl.h" +#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 + +using chromeos::dbus_utils::AsyncEventSequencer; + +namespace { + +const char kWaitForOwnershipTriggerSwitch[] = "wait_for_ownership_trigger"; + +class TpmManagerDaemon : public chromeos::DBusServiceDaemon { + public: + TpmManagerDaemon() + : chromeos::DBusServiceDaemon(tpm_manager::kTpmManagerServiceName) { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + 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_initializer_.get())); + } + + protected: + int OnInit() override { + int result = chromeos::DBusServiceDaemon::OnInit(); + if (result != EX_OK) { + LOG(ERROR) << "Error starting tpm_manager dbus daemon."; + return result; + } + CHECK(tpm_manager_service_->Initialize()); + return EX_OK; + } + + void RegisterDBusObjectsAsync(AsyncEventSequencer* sequencer) override { + dbus_service_.reset(new tpm_manager::DBusService( + bus_, tpm_manager_service_.get())); + dbus_service_->Register(sequencer->GetHandler("Register() failed.", true)); + } + + 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_; + + DISALLOW_COPY_AND_ASSIGN(TpmManagerDaemon); +}; + +} // namespace + +int main(int argc, char* argv[]) { + base::CommandLine::Init(argc, argv); + chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogToStderr); + TpmManagerDaemon daemon; + LOG(INFO) << "TpmManager Daemon Started."; + return daemon.Run(); +} diff --git a/server/mock_local_data_store.cc b/server/mock_local_data_store.cc new file mode 100644 index 0000000..e4978da --- /dev/null +++ b/server/mock_local_data_store.cc @@ -0,0 +1,23 @@ +// 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/mock_local_data_store.h" + +using testing::_; +using testing::DoAll; +using testing::Return; +using testing::SaveArg; +using testing::SetArgPointee; + +namespace tpm_manager { + +MockLocalDataStore::MockLocalDataStore() { + ON_CALL(*this, Read(_)) + .WillByDefault(DoAll(SetArgPointee<0>(fake_), Return(true))); + ON_CALL(*this, Write(_)) + .WillByDefault(DoAll(SaveArg<0>(&fake_), Return(true))); +} +MockLocalDataStore::~MockLocalDataStore() {} + +} // namespace tpm_manager diff --git a/server/mock_local_data_store.h b/server/mock_local_data_store.h new file mode 100644 index 0000000..5c22011 --- /dev/null +++ b/server/mock_local_data_store.h @@ -0,0 +1,28 @@ +// 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_MOCK_LOCAL_DATA_STORE_H_ +#define TPM_MANAGER_SERVER_MOCK_LOCAL_DATA_STORE_H_ + +#include "tpm_manager/server/local_data_store.h" + +#include <gmock/gmock.h> + +namespace tpm_manager { + +class MockLocalDataStore : public LocalDataStore { + public: + MockLocalDataStore(); + ~MockLocalDataStore() override; + + MOCK_METHOD1(Read, bool(LocalData*)); + MOCK_METHOD1(Write, bool(const LocalData&)); + + private: + LocalData fake_; +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_MOCK_LOCAL_DATA_STORE_H_ diff --git a/server/mock_tpm_initializer.cc b/server/mock_tpm_initializer.cc new file mode 100644 index 0000000..23614be --- /dev/null +++ b/server/mock_tpm_initializer.cc @@ -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. + +#include "tpm_manager/server/mock_tpm_initializer.h" + +using testing::Return; + +namespace tpm_manager { + +MockTpmInitializer::MockTpmInitializer() { + ON_CALL(*this, InitializeTpm()).WillByDefault(Return(true)); +} +MockTpmInitializer::~MockTpmInitializer() {} + +} // namespace tpm_manager diff --git a/server/mock_tpm_initializer.h b/server/mock_tpm_initializer.h new file mode 100644 index 0000000..81297b2 --- /dev/null +++ b/server/mock_tpm_initializer.h @@ -0,0 +1,24 @@ +// 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_MOCK_TPM_INITIALIZER_H_ +#define TPM_MANAGER_SERVER_MOCK_TPM_INITIALIZER_H_ + +#include "tpm_manager/server/tpm_initializer.h" + +#include <gmock/gmock.h> + +namespace tpm_manager { + +class MockTpmInitializer : public TpmInitializer { + public: + MockTpmInitializer(); + ~MockTpmInitializer() override; + + MOCK_METHOD0(InitializeTpm, bool()); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_MOCK_TPM_INITIALIZER_H_ diff --git a/server/mock_tpm_status.cc b/server/mock_tpm_status.cc new file mode 100644 index 0000000..345619a --- /dev/null +++ b/server/mock_tpm_status.cc @@ -0,0 +1,32 @@ +// 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/mock_tpm_status.h" + +using testing::_; +using testing::Invoke; +using testing::Return; + +namespace tpm_manager { + +bool GetDefaultDictionaryAttackInfo(int* counter, + int* threshold, + bool* lockout, + int* seconds_remaining) { + *counter = 0; + *threshold = 10; + *lockout = false; + *seconds_remaining = 0; + return true; +} + +MockTpmStatus::MockTpmStatus() { + ON_CALL(*this, IsTpmEnabled()).WillByDefault(Return(true)); + ON_CALL(*this, IsTpmOwned()).WillByDefault(Return(true)); + ON_CALL(*this, GetDictionaryAttackInfo(_, _, _, _)) + .WillByDefault(Invoke(GetDefaultDictionaryAttackInfo)); +} +MockTpmStatus::~MockTpmStatus() {} + +} // namespace tpm_manager diff --git a/server/mock_tpm_status.h b/server/mock_tpm_status.h new file mode 100644 index 0000000..963c47e --- /dev/null +++ b/server/mock_tpm_status.h @@ -0,0 +1,30 @@ +// 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_MOCK_TPM_STATUS_H_ +#define TPM_MANAGER_SERVER_MOCK_TPM_STATUS_H_ + +#include "tpm_manager/server/tpm_status.h" + +#include <gmock/gmock.h> + +namespace tpm_manager { + +class MockTpmStatus : public TpmStatus { + public: + MockTpmStatus(); + ~MockTpmStatus() override; + + MOCK_METHOD0(IsTpmEnabled, bool()); + MOCK_METHOD0(IsTpmOwned, bool()); + MOCK_METHOD4(GetDictionaryAttackInfo, + bool(int* counter, + int* threshold, + bool* lockout, + int* seconds_remaining)); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_MOCK_TPM_STATUS_H_ 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/org.chromium.TpmManager.conf b/server/org.chromium.TpmManager.conf new file mode 100644 index 0000000..70eca0f --- /dev/null +++ b/server/org.chromium.TpmManager.conf @@ -0,0 +1,15 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy user="tpm_manager"> + <allow own="org.chromium.TpmManager" /> + <allow send_destination="org.chromium.TpmManager" /> + </policy> + <policy context="default"> + <allow send_destination="org.chromium.TpmManager" /> + <!-- introspection denied --> + <deny send_destination="org.chromium.TpmManager" + send_interface="org.freedesktop.DBus.Introspectable" /> + </policy> +</busconfig> 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/tpm2_status_impl.cc b/server/tpm2_status_impl.cc new file mode 100644 index 0000000..53a9274 --- /dev/null +++ b/server/tpm2_status_impl.cc @@ -0,0 +1,75 @@ +// 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_status_impl.h" + +#include <base/logging.h> +#include <trunks/error_codes.h> +#include <trunks/tpm_generated.h> +#include <trunks/trunks_factory_impl.h> + +using trunks::TPM_RC; +using trunks::TPM_RC_SUCCESS; + +namespace tpm_manager { + +Tpm2StatusImpl::Tpm2StatusImpl() + : default_trunks_factory_(new trunks::TrunksFactoryImpl()), + trunks_factory_(default_trunks_factory_.get()), + trunks_tpm_state_(trunks_factory_->GetTpmState()) {} + +Tpm2StatusImpl::Tpm2StatusImpl(trunks::TrunksFactory* factory) + : trunks_factory_(factory), + trunks_tpm_state_(trunks_factory_->GetTpmState()) {} + +bool Tpm2StatusImpl::IsTpmEnabled() { + if (!initialized_) { + Refresh(); + } + return trunks_tpm_state_->IsEnabled(); +} + +bool Tpm2StatusImpl::IsTpmOwned() { + if (!is_owned_) { + Refresh(); + } + is_owned_ = trunks_tpm_state_->IsOwned(); + return is_owned_; +} + +bool Tpm2StatusImpl::GetDictionaryAttackInfo(int* counter, + int* threshold, + bool* lockout, + int* seconds_remaining) { + if (!Refresh()) { + return false; + } + if (counter) { + *counter = trunks_tpm_state_->GetLockoutCounter(); + } + if (threshold) { + *threshold = trunks_tpm_state_->GetLockoutThreshold(); + } + if (lockout) { + *lockout = trunks_tpm_state_->IsInLockout(); + } + if (seconds_remaining) { + *seconds_remaining = trunks_tpm_state_->GetLockoutCounter() * + trunks_tpm_state_->GetLockoutInterval(); + } + return true; +} + +bool Tpm2StatusImpl::Refresh() { + TPM_RC result = trunks_tpm_state_->Initialize(); + if (result != TPM_RC_SUCCESS) { + LOG(WARNING) << "Error initializing trunks tpm state: " + << trunks::GetErrorString(result); + return false; + } + initialized_ = true; + return true; +} + +} // namespace tpm_manager diff --git a/server/tpm2_status_impl.h b/server/tpm2_status_impl.h new file mode 100644 index 0000000..0bde61c --- /dev/null +++ b/server/tpm2_status_impl.h @@ -0,0 +1,50 @@ +// 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_STATUS_IMPL_H_ +#define TPM_MANAGER_SERVER_TPM2_STATUS_IMPL_H_ + +#include "tpm_manager/server/tpm_status.h" + +#include <memory> + +#include <base/macros.h> +#include <trunks/tpm_state.h> +#include <trunks/trunks_factory.h> + +namespace tpm_manager { + +class Tpm2StatusImpl : public TpmStatus { + public: + Tpm2StatusImpl(); + // Does not take ownership of |factory|. + explicit Tpm2StatusImpl(trunks::TrunksFactory* factory); + ~Tpm2StatusImpl() override = default; + + // TpmState methods. + bool IsTpmEnabled() override; + bool IsTpmOwned() override; + bool GetDictionaryAttackInfo(int* counter, + int* threshold, + bool* lockout, + int* seconds_remaining) override; + + private: + // Refreshes the Tpm state information. Can be called as many times as needed + // to refresh the cached information in this class. Return true if the + // refresh operation succeeded. + bool Refresh(); + + bool initialized_{false}; + bool is_owned_{false}; + std::unique_ptr<trunks::TrunksFactory> default_trunks_factory_; + trunks::TrunksFactory* trunks_factory_; + scoped_ptr<trunks::TpmState> trunks_tpm_state_; + + DISALLOW_COPY_AND_ASSIGN(Tpm2StatusImpl); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_TPM2_STATUS_IMPL_H_ diff --git a/server/tpm2_status_test.cc b/server/tpm2_status_test.cc new file mode 100644 index 0000000..82da4ba --- /dev/null +++ b/server/tpm2_status_test.cc @@ -0,0 +1,147 @@ +// 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_status_impl.h" + +#include <memory> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <trunks/mock_tpm_state.h> +#include <trunks/trunks_factory_for_test.h> + +using testing::NiceMock; +using testing::Return; +using trunks::TPM_RC_FAILURE; +using trunks::TPM_RC_SUCCESS; + +namespace tpm_manager { + +class Tpm2StatusTest : public testing::Test { + public: + Tpm2StatusTest() : factory_(new trunks::TrunksFactoryForTest()) {} + virtual ~Tpm2StatusTest() = default; + + void SetUp() { + factory_->set_tpm_state(&mock_tpm_state_); + tpm_status_.reset(new Tpm2StatusImpl(factory_.get())); + } + + protected: + NiceMock<trunks::MockTpmState> mock_tpm_state_; + std::unique_ptr<trunks::TrunksFactoryForTest> factory_; + std::unique_ptr<TpmStatus> tpm_status_; +}; + + +TEST_F(Tpm2StatusTest, IsEnabledSuccess) { + EXPECT_CALL(mock_tpm_state_, Initialize()) + .WillRepeatedly(Return(TPM_RC_SUCCESS)); + EXPECT_CALL(mock_tpm_state_, IsEnabled()) + .WillRepeatedly(Return(true)); + EXPECT_TRUE(tpm_status_->IsTpmEnabled()); +} + +TEST_F(Tpm2StatusTest, IsEnabledFailure) { + EXPECT_CALL(mock_tpm_state_, IsEnabled()) + .WillRepeatedly(Return(false)); + EXPECT_FALSE(tpm_status_->IsTpmEnabled()); +} + +TEST_F(Tpm2StatusTest, IsEnabledNoRepeatedInitialization) { + EXPECT_CALL(mock_tpm_state_, Initialize()) + .WillOnce(Return(TPM_RC_SUCCESS)); + EXPECT_TRUE(tpm_status_->IsTpmEnabled()); + EXPECT_TRUE(tpm_status_->IsTpmEnabled()); +} + +TEST_F(Tpm2StatusTest, IsOwnedSuccess) { + EXPECT_CALL(mock_tpm_state_, Initialize()) + .WillRepeatedly(Return(TPM_RC_SUCCESS)); + EXPECT_CALL(mock_tpm_state_, IsOwned()) + .WillRepeatedly(Return(true)); + EXPECT_TRUE(tpm_status_->IsTpmOwned()); +} + +TEST_F(Tpm2StatusTest, IsOwnedFailure) { + EXPECT_CALL(mock_tpm_state_, IsOwned()) + .WillRepeatedly(Return(false)); + EXPECT_FALSE(tpm_status_->IsTpmOwned()); +} + +TEST_F(Tpm2StatusTest, IsOwnedRepeatedInitializationOnFalse) { + EXPECT_CALL(mock_tpm_state_, Initialize()) + .Times(2) + .WillRepeatedly(Return(TPM_RC_SUCCESS)); + EXPECT_CALL(mock_tpm_state_, IsOwned()) + .WillOnce(Return(false)); + EXPECT_FALSE(tpm_status_->IsTpmOwned()); + EXPECT_CALL(mock_tpm_state_, IsOwned()) + .WillRepeatedly(Return(true)); + EXPECT_TRUE(tpm_status_->IsTpmOwned()); +} + +TEST_F(Tpm2StatusTest, IsOwnedNoRepeatedInitializationOnTrue) { + EXPECT_CALL(mock_tpm_state_, Initialize()) + .WillOnce(Return(TPM_RC_SUCCESS)); + EXPECT_CALL(mock_tpm_state_, IsOwned()) + .WillRepeatedly(Return(true)); + EXPECT_TRUE(tpm_status_->IsTpmOwned()); + EXPECT_TRUE(tpm_status_->IsTpmOwned()); +} + +TEST_F(Tpm2StatusTest, GetDictionaryAttackInfoInitializeFailure) { + EXPECT_CALL(mock_tpm_state_, Initialize()) + .WillRepeatedly(Return(TPM_RC_FAILURE)); + int count; + int threshold; + bool lockout; + int seconds_remaining; + EXPECT_FALSE(tpm_status_->GetDictionaryAttackInfo(&count, + &threshold, + &lockout, + &seconds_remaining)); +} + +TEST_F(Tpm2StatusTest, GetDictionaryAttackInfoForwarding) { + int lockout_count = 3; + int lockout_threshold = 16; + bool is_locked = true; + int lockout_interval = 3600; + EXPECT_CALL(mock_tpm_state_, GetLockoutCounter()) + .WillRepeatedly(Return(lockout_count)); + EXPECT_CALL(mock_tpm_state_, GetLockoutThreshold()) + .WillRepeatedly(Return(lockout_threshold)); + EXPECT_CALL(mock_tpm_state_, IsInLockout()) + .WillRepeatedly(Return(is_locked)); + EXPECT_CALL(mock_tpm_state_, GetLockoutInterval()) + .WillRepeatedly(Return(lockout_interval)); + int count; + int threshold; + bool lockout; + int seconds_remaining; + EXPECT_TRUE(tpm_status_->GetDictionaryAttackInfo(&count, + &threshold, + &lockout, + &seconds_remaining)); + EXPECT_EQ(count, lockout_count); + EXPECT_EQ(threshold, lockout_threshold); + EXPECT_EQ(lockout, is_locked); + EXPECT_EQ(seconds_remaining, lockout_count * lockout_interval); +} + +TEST_F(Tpm2StatusTest, GetDictionaryAttackInfoAlwaysRefresh) { + EXPECT_CALL(mock_tpm_state_, Initialize()) + .WillRepeatedly(Return(TPM_RC_SUCCESS)); + int count; + int threshold; + bool lockout; + int seconds_remaining; + EXPECT_TRUE(tpm_status_->GetDictionaryAttackInfo(&count, + &threshold, + &lockout, + &seconds_remaining)); +} + +} // namespace tpm_manager diff --git a/server/tpm_connection.cc b/server/tpm_connection.cc new file mode 100644 index 0000000..ee57f3a --- /dev/null +++ b/server/tpm_connection.cc @@ -0,0 +1,96 @@ +// 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_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; +const int kTpmConnectIntervalMs = 100; + +} // namespace + +namespace tpm_manager { + +TSS_HCONTEXT TpmConnection::GetContext() { + if (!ConnectContextIfNeeded()) { + return 0; + } + return context_.value(); +} + +TSS_HTPM TpmConnection::GetTpm() { + if (!ConnectContextIfNeeded()) { + return 0; + } + TSS_RESULT result; + TSS_HTPM tpm_handle; + if (TPM_ERROR(result = Tspi_Context_GetTpmObject(context_.value(), + &tpm_handle))) { + TPM_LOG(ERROR, result) << "Error getting a handle to the TPM."; + return 0; + } + return tpm_handle; +} + +TSS_HTPM TpmConnection::GetTpmWithAuth(const std::string& owner_password) { + TSS_HTPM tpm_handle = GetTpm(); + if (tpm_handle == 0) { + return 0; + } + 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() { + if (context_.value() != 0) { + return true; + } + TSS_RESULT result; + if (TPM_ERROR(result = Tspi_Context_Create(context_.ptr()))) { + TPM_LOG(ERROR, result) << "Error connecting to TPM."; + return false; + } + // We retry on failure. It might be that tcsd is starting up. + for (int i = 0; i < kTpmConnectRetries; i++) { + if (TPM_ERROR(result = Tspi_Context_Connect(context_, nullptr))) { + if (ERROR_CODE(result) == TSS_E_COMM_FAILURE) { + base::PlatformThread::Sleep( + base::TimeDelta::FromMilliseconds(kTpmConnectIntervalMs)); + } else { + TPM_LOG(ERROR, result) << "Error connecting to TPM."; + return false; + } + } else { + break; + } + } + return (context_.value() != 0); +} + +} // namespace tpm_manager diff --git a/server/tpm_connection.h b/server/tpm_connection.h new file mode 100644 index 0000000..833a972 --- /dev/null +++ b/server/tpm_connection.h @@ -0,0 +1,43 @@ +// 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_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 { + public: + TpmConnection() = default; + ~TpmConnection() = default; + + // 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(); + + trousers::ScopedTssContext context_; + + DISALLOW_COPY_AND_ASSIGN(TpmConnection); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_TPM_CONNECTION_H_ diff --git a/server/tpm_initializer.h b/server/tpm_initializer.h new file mode 100644 index 0000000..34aa0d0 --- /dev/null +++ b/server/tpm_initializer.h @@ -0,0 +1,25 @@ +// 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_H_ +#define TPM_MANAGER_SERVER_TPM_INITIALIZER_H_ + +namespace tpm_manager { + +// TpmInitializer performs initialization tasks on some kind of TPM device. +class TpmInitializer { + public: + TpmInitializer() = default; + virtual ~TpmInitializer() = default; + + // Initializes a TPM and returns true on success. If the TPM is already + // initialized, this method has no effect and succeeds. If the TPM is + // partially initialized, e.g. the process was previously interrupted, then + // the process picks up where it left off. + virtual bool InitializeTpm() = 0; +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_TPM_INITIALIZER_H_ 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 new file mode 100644 index 0000000..641bce6 --- /dev/null +++ b/server/tpm_manager-seccomp-amd64.policy @@ -0,0 +1,63 @@ +# 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. + +# Tested on link +gettid: 1 +getuid: 1 +geteuid: 1 +getgid: 1 +getegid: 1 +getresuid: 1 +getresgid: 1 + +clock_getres: 1 +clock_gettime: 1 +gettimeofday: 1 +time: 1 + +# Allow socket(domain==PF_LOCAL) or socket(domain==PF_NETLINK) +socket: arg0 == 0x1 || arg0 == 0x10 +socketpair: 1 +connect: 1 +getsockname: 1 +pipe: 1 +sendmsg: 1 +sendto: 1 +recvmsg: 1 +recvfrom: 1 + +epoll_create: 1 +epoll_wait: 1 +epoll_ctl: 1 +poll: 1 + +open: 1 +read: 1 +write: 1 +close: 1 + +fstat: 1 +stat: 1 +lseek: 1 +fcntl: 1 + +futex: 1 +set_robust_list: 1 +restart_syscall: 1 +exit: 1 +exit_group: 1 +rt_sigreturn: 1 +rt_sigprocmask: 1 +signalfd4: 1 + +brk: 1 +mmap: 1 +madvise: 1 +mprotect: 1 +munmap: 1 + +clone: 1 +# These calls are attempted but apparently not necessary; return EPERM. +prctl: return 1 +ioctl: return 1 diff --git a/server/tpm_manager-seccomp-arm.policy b/server/tpm_manager-seccomp-arm.policy new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/server/tpm_manager-seccomp-arm.policy diff --git a/server/tpm_manager-seccomp-x86.policy b/server/tpm_manager-seccomp-x86.policy new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/server/tpm_manager-seccomp-x86.policy diff --git a/server/tpm_manager_service.cc b/server/tpm_manager_service.cc new file mode 100644 index 0000000..f7077d4 --- /dev/null +++ b/server/tpm_manager_service.cc @@ -0,0 +1,114 @@ +// 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_manager_service.h" + +#include <base/callback.h> +#include <base/command_line.h> +#include <chromeos/bind_lambda.h> + +namespace tpm_manager { + +TpmManagerService::TpmManagerService(bool wait_for_ownership, + LocalDataStore* local_data_store, + 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) { +} + +bool TpmManagerService::Initialize() { + LOG(INFO) << "TpmManager service started."; + worker_thread_.reset(new base::Thread("TpmManager Service Worker")); + worker_thread_->StartWithOptions( + base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); + base::Closure task = base::Bind(&TpmManagerService::InitializeTask, + base::Unretained(this)); + worker_thread_->task_runner()->PostNonNestableTask(FROM_HERE, task); + return true; +} + +void TpmManagerService::InitializeTask() { + if (!tpm_status_->IsTpmEnabled()) { + LOG(WARNING) << __func__ << ": TPM is disabled."; + return; + } + if (!wait_for_ownership_) { + VLOG(1) << "Initializing TPM."; + if (!tpm_initializer_->InitializeTpm()) { + LOG(WARNING) << __func__ << ": TPM initialization failed."; + return; + } + } +} + +void TpmManagerService::GetTpmStatus(const GetTpmStatusRequest& request, + const GetTpmStatusCallback& callback) { + auto result = std::make_shared<GetTpmStatusReply>(); + base::Closure task = base::Bind(&TpmManagerService::GetTpmStatusTask, + base::Unretained(this), request, result); + base::Closure reply = base::Bind( + &TpmManagerService::TaskRelayCallback<GetTpmStatusReply>, + weak_factory_.GetWeakPtr(), + callback, + result); + worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); +} + +void TpmManagerService::GetTpmStatusTask( + const GetTpmStatusRequest& request, + const std::shared_ptr<GetTpmStatusReply>& result) { + VLOG(1) << __func__; + result->set_enabled(tpm_status_->IsTpmEnabled()); + result->set_owned(tpm_status_->IsTpmOwned()); + LocalData local_data; + if (local_data_store_ && local_data_store_->Read(&local_data)) { + *result->mutable_local_data() = local_data; + } + int counter; + int threshold; + bool lockout; + int lockout_time_remaining; + if (tpm_status_->GetDictionaryAttackInfo(&counter, &threshold, &lockout, + &lockout_time_remaining)) { + result->set_dictionary_attack_counter(counter); + result->set_dictionary_attack_threshold(threshold); + result->set_dictionary_attack_lockout_in_effect(lockout); + result->set_dictionary_attack_lockout_seconds_remaining( + lockout_time_remaining); + } +} + +void TpmManagerService::TakeOwnership(const TakeOwnershipRequest& request, + const TakeOwnershipCallback& callback) { + auto result = std::make_shared<TakeOwnershipReply>(); + base::Closure task = base::Bind(&TpmManagerService::TakeOwnershipTask, + base::Unretained(this), request, result); + base::Closure reply = base::Bind( + &TpmManagerService::TaskRelayCallback<TakeOwnershipReply>, + weak_factory_.GetWeakPtr(), + callback, + result); + worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); +} + +void TpmManagerService::TakeOwnershipTask( + const TakeOwnershipRequest& request, + const std::shared_ptr<TakeOwnershipReply>& result) { + VLOG(1) << __func__; + if (!tpm_status_->IsTpmEnabled()) { + result->set_status(STATUS_NOT_AVAILABLE); + return; + } + if (!tpm_initializer_->InitializeTpm()) { + 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 new file mode 100644 index 0000000..d715816 --- /dev/null +++ b/server/tpm_manager_service.h @@ -0,0 +1,103 @@ +// 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_MANAGER_SERVICE_H_ +#define TPM_MANAGER_SERVER_TPM_MANAGER_SERVICE_H_ + +#include "tpm_manager/common/tpm_manager_interface.h" + +#include <memory> + +#include <base/callback.h> +#include <base/macros.h> +#include <base/memory/weak_ptr.h> +#include <base/threading/thread.h> +#include <chromeos/bind_lambda.h> + +#include "tpm_manager/server/local_data_store.h" +#include "tpm_manager/server/tpm_initializer.h" +#include "tpm_manager/server/tpm_status.h" + +namespace tpm_manager { + +// This class implements the core tpm_manager service. All Tpm access is +// asynchronous, except for the initial setup in Initialize(). +// Usage: +// std::unique_ptr<TpmManagerInterface> tpm_manager = new TpmManagerService(); +// CHECK(tpm_manager->Initialize()); +// tpm_manager->GetTpmStatus(...); +// +// THREADING NOTES: +// This class runs a worker thread and delegates all calls to it. This keeps the +// public methods non-blocking while allowing complex implementation details +// with dependencies on the TPM, network, and filesystem to be coded in a more +// readable way. It also serves to serialize method execution which reduces +// complexity with TPM state. +// +// Tasks that run on the worker thread are bound with base::Unretained which is +// safe because the thread is owned by this class (so it is guaranteed not to +// process a task after destruction). Weak pointers are used to post replies +// back to the main thread. +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|, |tpm_status| or |tpm_initializer|. + explicit TpmManagerService(bool wait_for_ownership, + LocalDataStore* local_data_store, + TpmStatus* tpm_status, + TpmInitializer* tpm_initializer); + ~TpmManagerService() override = default; + + // TpmManagerInterface methods. + bool Initialize() override; + void GetTpmStatus(const GetTpmStatusRequest& request, + const GetTpmStatusCallback& callback) override; + void TakeOwnership(const TakeOwnershipRequest& request, + const TakeOwnershipCallback& callback) override; + + private: + // A relay callback which allows the use of weak pointer semantics for a reply + // to TaskRunner::PostTaskAndReply. + template<typename ReplyProtobufType> + void TaskRelayCallback( + const base::Callback<void(const ReplyProtobufType&)> callback, + const std::shared_ptr<ReplyProtobufType>& reply) { + callback.Run(*reply); + } + + // Blocking implementation of GetTpmStatus that can be executed on the + // background worker thread. + void GetTpmStatusTask(const GetTpmStatusRequest& request, + const std::shared_ptr<GetTpmStatusReply>& result); + + // Blocking implementation of TakeOwnership that can be executed on the + // background worker thread. + void TakeOwnershipTask(const TakeOwnershipRequest& request, + const std::shared_ptr<TakeOwnershipReply>& result); + + // Synchronously initializes the TPM according to the current configuration. + // If an initialization process was interrupted it will be continued. If the + // TPM is already initialized or cannot yet be initialized, this method has no + // effect. + void InitializeTask(); + + LocalDataStore* local_data_store_; + 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_; + // Background thread to allow processing of potentially lengthy TPM requests + // in the background. + std::unique_ptr<base::Thread> worker_thread_; + // Declared last so any weak pointers are destroyed first. + base::WeakPtrFactory<TpmManagerService> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(TpmManagerService); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_TPM_MANAGER_SERVICE_H_ diff --git a/server/tpm_manager_service_test.cc b/server/tpm_manager_service_test.cc new file mode 100644 index 0000000..d1c36fb --- /dev/null +++ b/server/tpm_manager_service_test.cc @@ -0,0 +1,226 @@ +// 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 <base/run_loop.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "tpm_manager/server/mock_local_data_store.h" +#include "tpm_manager/server/mock_tpm_initializer.h" +#include "tpm_manager/server/mock_tpm_status.h" +#include "tpm_manager/server/tpm_manager_service.h" + +using testing::_; +using testing::AtLeast; +using testing::Invoke; +using testing::NiceMock; +using testing::Return; +using testing::SetArgPointee; + +namespace tpm_manager { + +// A test fixture that takes care of message loop management and configuring a +// TpmManagerService instance with mock dependencies. +class TpmManagerServiceTest : public testing::Test { + public: + ~TpmManagerServiceTest() override = default; + void SetUp() override { + service_.reset(new TpmManagerService(true /*wait_for_ownership*/, + &mock_local_data_store_, + &mock_tpm_status_, + &mock_tpm_initializer_)); + SetupService(); + } + + protected: + void Run() { + run_loop_.Run(); + } + + void RunServiceWorkerAndQuit() { + // Run out the service worker loop by posting a new command and waiting for + // the response. + auto callback = [this](const GetTpmStatusReply& reply) { + Quit(); + }; + GetTpmStatusRequest request; + service_->GetTpmStatus(request, base::Bind(callback)); + Run(); + } + + void Quit() { + run_loop_.Quit(); + } + + void SetupService() { + CHECK(service_->Initialize()); + } + + NiceMock<MockLocalDataStore> mock_local_data_store_; + NiceMock<MockTpmInitializer> mock_tpm_initializer_; + NiceMock<MockTpmStatus> mock_tpm_status_; + std::unique_ptr<TpmManagerService> service_; + + private: + base::MessageLoop message_loop_; + base::RunLoop run_loop_; +}; + +// Tests must call SetupService(). +class TpmManagerServiceTest_NoWaitForOwnership : public TpmManagerServiceTest { + public: + ~TpmManagerServiceTest_NoWaitForOwnership() override = default; + void SetUp() override { + service_.reset(new TpmManagerService(false /*wait_for_ownership*/, + &mock_local_data_store_, + &mock_tpm_status_, + &mock_tpm_initializer_)); + } +}; + +TEST_F(TpmManagerServiceTest_NoWaitForOwnership, AutoInitialize) { + // Make sure InitializeTpm doesn't get multiple calls. + EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()).Times(1); + SetupService(); + RunServiceWorkerAndQuit(); +} + +TEST_F(TpmManagerServiceTest_NoWaitForOwnership, AutoInitializeNoTpm) { + EXPECT_CALL(mock_tpm_status_, IsTpmEnabled()).WillRepeatedly(Return(false)); + EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()).Times(0); + SetupService(); + RunServiceWorkerAndQuit(); +} + +TEST_F(TpmManagerServiceTest_NoWaitForOwnership, AutoInitializeFailure) { + EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()) + .WillRepeatedly(Return(false)); + SetupService(); + RunServiceWorkerAndQuit(); +} + +TEST_F(TpmManagerServiceTest, NoAutoInitialize) { + EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()).Times(0); + RunServiceWorkerAndQuit(); +} + +TEST_F(TpmManagerServiceTest, GetTpmStatusSuccess) { + EXPECT_CALL(mock_tpm_status_, GetDictionaryAttackInfo(_, _, _, _)) + .WillRepeatedly(Invoke([](int* counter, int* threshold, bool* lockout, + int* seconds_remaining) { + *counter = 5; + *threshold = 6; + *lockout = true; + *seconds_remaining = 7; + return true; + })); + LocalData local_data; + local_data.set_owner_password("test"); + EXPECT_CALL(mock_local_data_store_, Read(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(local_data), Return(true))); + + auto callback = [this](const GetTpmStatusReply& reply) { + EXPECT_EQ(STATUS_SUCCESS, reply.status()); + EXPECT_TRUE(reply.enabled()); + EXPECT_TRUE(reply.owned()); + EXPECT_EQ("test", reply.local_data().owner_password()); + EXPECT_EQ(5, reply.dictionary_attack_counter()); + EXPECT_EQ(6, reply.dictionary_attack_threshold()); + EXPECT_TRUE(reply.dictionary_attack_lockout_in_effect()); + EXPECT_EQ(7, reply.dictionary_attack_lockout_seconds_remaining()); + Quit(); + }; + GetTpmStatusRequest request; + service_->GetTpmStatus(request, base::Bind(callback)); + Run(); +} + +TEST_F(TpmManagerServiceTest, GetTpmStatusLocalDataFailure) { + EXPECT_CALL(mock_local_data_store_, Read(_)) + .WillRepeatedly(Return(false)); + auto callback = [this](const GetTpmStatusReply& reply) { + EXPECT_EQ(STATUS_SUCCESS, reply.status()); + EXPECT_TRUE(reply.enabled()); + EXPECT_TRUE(reply.owned()); + EXPECT_FALSE(reply.has_local_data()); + EXPECT_TRUE(reply.has_dictionary_attack_counter()); + EXPECT_TRUE(reply.has_dictionary_attack_threshold()); + EXPECT_TRUE(reply.has_dictionary_attack_lockout_in_effect()); + EXPECT_TRUE(reply.has_dictionary_attack_lockout_seconds_remaining()); + Quit(); + }; + GetTpmStatusRequest request; + service_->GetTpmStatus(request, base::Bind(callback)); + Run(); +} + +TEST_F(TpmManagerServiceTest, GetTpmStatusNoTpm) { + EXPECT_CALL(mock_tpm_status_, IsTpmEnabled()).WillRepeatedly(Return(false)); + EXPECT_CALL(mock_tpm_status_, GetDictionaryAttackInfo(_, _, _, _)) + .WillRepeatedly(Return(false)); + auto callback = [this](const GetTpmStatusReply& reply) { + EXPECT_EQ(STATUS_SUCCESS, reply.status()); + EXPECT_FALSE(reply.enabled()); + EXPECT_TRUE(reply.owned()); + EXPECT_TRUE(reply.has_local_data()); + EXPECT_FALSE(reply.has_dictionary_attack_counter()); + EXPECT_FALSE(reply.has_dictionary_attack_threshold()); + EXPECT_FALSE(reply.has_dictionary_attack_lockout_in_effect()); + EXPECT_FALSE(reply.has_dictionary_attack_lockout_seconds_remaining()); + Quit(); + }; + GetTpmStatusRequest request; + service_->GetTpmStatus(request, base::Bind(callback)); + Run(); +} + +TEST_F(TpmManagerServiceTest, TakeOwnershipSuccess) { + // Make sure InitializeTpm doesn't get multiple calls. + EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()).Times(1); + auto callback = [this](const TakeOwnershipReply& reply) { + EXPECT_EQ(STATUS_SUCCESS, reply.status()); + Quit(); + }; + TakeOwnershipRequest request; + service_->TakeOwnership(request, base::Bind(callback)); + Run(); +} + +TEST_F(TpmManagerServiceTest, TakeOwnershipFailure) { + EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()) + .WillRepeatedly(Return(false)); + auto callback = [this](const TakeOwnershipReply& reply) { + EXPECT_EQ(STATUS_UNEXPECTED_DEVICE_ERROR, reply.status()); + Quit(); + }; + TakeOwnershipRequest request; + service_->TakeOwnership(request, base::Bind(callback)); + Run(); +} + +TEST_F(TpmManagerServiceTest, TakeOwnershipNoTpm) { + EXPECT_CALL(mock_tpm_status_, IsTpmEnabled()).WillRepeatedly(Return(false)); + auto callback = [this](const TakeOwnershipReply& reply) { + EXPECT_EQ(STATUS_NOT_AVAILABLE, reply.status()); + Quit(); + }; + TakeOwnershipRequest request; + service_->TakeOwnership(request, base::Bind(callback)); + Run(); +} + +TEST_F(TpmManagerServiceTest_NoWaitForOwnership, + TakeOwnershipAfterAutoInitialize) { + EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()).Times(AtLeast(2)); + SetupService(); + auto callback = [this](const TakeOwnershipReply& reply) { + EXPECT_EQ(STATUS_SUCCESS, reply.status()); + Quit(); + }; + TakeOwnershipRequest request; + service_->TakeOwnership(request, base::Bind(callback)); + Run(); +} + +} // namespace tpm_manager diff --git a/server/tpm_managerd.conf b/server/tpm_managerd.conf new file mode 100644 index 0000000..60b5bc8 --- /dev/null +++ b/server/tpm_managerd.conf @@ -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. + +description "Chromium OS device tpm_manager service." +author "chromium-os-dev@chromium.org" + +start on starting system-services +stop on stopping system-services +respawn + +# Minijail forks off our process +expect fork + +exec minijail0 -i -g tpm_manager -u tpm_manager -G -n \ + -S /usr/share/policy/tpm_managerd-seccomp.policy /usr/sbin/tpm_managerd diff --git a/server/tpm_status.h b/server/tpm_status.h new file mode 100644 index 0000000..a373f01 --- /dev/null +++ b/server/tpm_status.h @@ -0,0 +1,30 @@ +// 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_STATUS_H_ +#define TPM_MANAGER_SERVER_TPM_STATUS_H_ + +namespace tpm_manager { + +// TpmStatus is an interface class that reports status information for some kind +// of TPM device. +class TpmStatus { + public: + TpmStatus() = default; + virtual ~TpmStatus() = default; + + // Returns true iff the TPM is enabled. + virtual bool IsTpmEnabled() = 0; + // Returns true iff the TPM has been owned. + virtual bool IsTpmOwned() = 0; + // Reports the current state of the TPM dictionary attack logic. + virtual bool GetDictionaryAttackInfo(int* counter, + int* threshold, + bool* lockout, + int* seconds_remaining) = 0; +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_TPM_STATUS_H_ diff --git a/server/tpm_status_impl.cc b/server/tpm_status_impl.cc new file mode 100644 index 0000000..f390e68 --- /dev/null +++ b/server/tpm_status_impl.cc @@ -0,0 +1,104 @@ +// 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_status_impl.h" + +#include <vector> + +#include <base/logging.h> +#include <trousers/tss.h> +#include <trousers/trousers.h> // NOLINT(build/include_alpha) + +namespace tpm_manager { + +// Minimum size of TPM_DA_INFO struct. +const size_t kMinimumDaInfoSize = 21; + +bool TpmStatusImpl::IsTpmEnabled() { + if (!is_enable_initialized_) { + RefreshOwnedEnabledInfo(); + } + return is_enabled_; +} + +bool TpmStatusImpl::IsTpmOwned() { + if (!is_owned_) { + RefreshOwnedEnabledInfo(); + } + return is_owned_; +} + +bool TpmStatusImpl::GetDictionaryAttackInfo(int* counter, + int* threshold, + bool* lockout, + int* seconds_remaining) { + std::string capability_data; + if (!GetCapability(TSS_TPMCAP_DA_LOGIC, TPM_ET_KEYHANDLE, + &capability_data, nullptr) || + capability_data.size() < kMinimumDaInfoSize) { + LOG(ERROR) << "Error getting tpm capability data."; + return false; + } + if (static_cast<uint16_t>(capability_data[1]) == TPM_TAG_DA_INFO) { + TPM_DA_INFO da_info; + uint64_t offset = 0; + std::vector<BYTE> bytes(capability_data.begin(), capability_data.end()); + Trspi_UnloadBlob_DA_INFO(&offset, bytes.data(), &da_info); + if (counter) { *counter = da_info.currentCount; } + if (threshold) { *threshold = da_info.thresholdCount; } + if (lockout) { *lockout = (da_info.state == TPM_DA_STATE_ACTIVE); } + if (seconds_remaining) { *seconds_remaining = da_info.actionDependValue; } + } + return true; +} + +void TpmStatusImpl::RefreshOwnedEnabledInfo() { + TSS_RESULT result; + std::string capability_data; + if (!GetCapability(TSS_TPMCAP_PROPERTY, TSS_TPMCAP_PROP_OWNER, + &capability_data, &result)) { + if (ERROR_CODE(result) == TPM_E_DISABLED) { + is_enable_initialized_ = true; + is_enabled_ = false; + } + } else { + is_enable_initialized_ = true; + is_enabled_ = true; + // |capability_data| should be populated with a TSS_BOOL which is true iff + // the Tpm is owned. + if (capability_data.size() != sizeof(TSS_BOOL)) { + LOG(ERROR) << "Error refreshing Tpm ownership information."; + return; + } + is_owned_ = (capability_data[0] != 0); + } +} + +bool TpmStatusImpl::GetCapability(uint32_t capability, + uint32_t sub_capability, + std::string* data, + TSS_RESULT* tpm_result) { + CHECK(data); + TSS_HTPM tpm_handle = tpm_connection_.GetTpm(); + if (tpm_handle == 0) { + return false; + } + uint32_t length = 0; + trousers::ScopedTssMemory buf(tpm_connection_.GetContext()); + TSS_RESULT result = Tspi_TPM_GetCapability( + tpm_handle, capability, sizeof(uint32_t), + reinterpret_cast<BYTE*>(&sub_capability), + &length, buf.ptr()); + if (tpm_result) { + *tpm_result = result; + } + if (TPM_ERROR(result)) { + LOG(ERROR) << "Error getting TPM capability data."; + return false; + } + data->assign(buf.value(), buf.value() + length); + return true; +} + +} // namespace tpm_manager diff --git a/server/tpm_status_impl.h b/server/tpm_status_impl.h new file mode 100644 index 0000000..cbc3c88 --- /dev/null +++ b/server/tpm_status_impl.h @@ -0,0 +1,56 @@ +// 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_STATUS_IMPL_H_ +#define TPM_MANAGER_SERVER_TPM_STATUS_IMPL_H_ + +#include "tpm_manager/server/tpm_status.h" + +#include <memory> +#include <string> + +#include <base/macros.h> +#include <trousers/tss.h> +#include <trousers/trousers.h> // NOLINT(build/include_alpha) + +#include <tpm_manager/server/tpm_connection.h> + +namespace tpm_manager { + +class TpmStatusImpl : public TpmStatus { + public: + TpmStatusImpl() = default; + ~TpmStatusImpl() override = default; + + // TpmState methods. + bool IsTpmEnabled() override; + bool IsTpmOwned() override; + bool GetDictionaryAttackInfo(int* counter, + int* threshold, + bool* lockout, + int* seconds_remaining) override; + + private: + // This method refreshes the |is_owned_| and |is_enabled_| status of the + // Tpm. It can be called multiple times. + void RefreshOwnedEnabledInfo(); + // This method wraps calls to Tspi_TPM_GetCapability. |data| is set to + // the raw capability data. If the optional out argument |tpm_result| is + // provided, it is set to the result of the |Tspi_TPM_GetCapability| call. + bool GetCapability(uint32_t capability, + uint32_t sub_capability, + std::string* data, + TSS_RESULT* tpm_result); + + TpmConnection tpm_connection_; + bool is_enabled_{false}; + bool is_owned_{false}; + bool is_enable_initialized_{false}; + + DISALLOW_COPY_AND_ASSIGN(TpmStatusImpl); +}; + +} // namespace tpm_manager + +#endif // TPM_MANAGER_SERVER_TPM_STATUS_IMPL_H_ 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 new file mode 100644 index 0000000..1f213ad --- /dev/null +++ b/tpm_manager.gyp @@ -0,0 +1,163 @@ +# 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. + +{ + 'target_defaults': { + 'includes': ['../common-mk/common.gypi'], + 'variables': { + 'deps': [ # This is a list of pkg-config dependencies + 'libchrome-<(libbase_ver)', + 'libchromeos-<(libbase_ver)', + 'openssl', + 'protobuf-lite', + ], + }, + 'defines': [ 'USE_TPM2=<(USE_tpm2)' ], + }, + 'targets': [ + # A library for just the protobufs. + { + 'target_name': 'proto_library', + 'type': 'static_library', + 'variables': { + 'proto_in_dir': 'common', + 'proto_out_dir': 'include/tpm_manager/common', + }, + 'sources': [ + '<(proto_in_dir)/dbus_interface.proto', + '<(proto_in_dir)/local_data.proto', + 'common/print_dbus_interface_proto.cc', + 'common/print_local_data_proto.cc', + ], + 'includes': ['../common-mk/protoc.gypi'], + }, + # A shared library for clients. + { + 'target_name': 'libtpm_manager', + 'type': 'shared_library', + 'sources': [ + 'client/dbus_proxy.cc', + ], + 'dependencies': [ + 'proto_library', + ], + }, + # A client command line utility. + { + 'target_name': 'tpm_manager_client', + 'type': 'executable', + 'sources': [ + 'client/main.cc', + ], + 'dependencies': [ + 'libtpm_manager', + 'proto_library', + ] + }, + # A library for server code. + { + 'target_name': 'server_library', + 'type': 'static_library', + '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': [ + '-ltrunks', + ], + }, + }], + ['USE_tpm2 == 0', { + 'sources': [ + 'server/tpm_connection.cc', + 'server/tpm_status_impl.cc', + 'server/tpm_initializer_impl.cc', + ], + 'all_dependent_settings': { + 'libraries': [ + '-ltspi', + ], + }, + }], + ], + 'dependencies': [ + 'proto_library', + ], + }, + # The tpm_manager daemon. + { + 'target_name': 'tpm_managerd', + 'type': 'executable', + 'sources': [ + 'server/main.cc', + ], + 'variables': { + 'deps': [ + 'libminijail', + ], + }, + 'dependencies': [ + 'proto_library', + 'server_library', + ], + }, + ], + 'conditions': [ + ['USE_test == 1', { + 'targets': [ + { + 'target_name': 'tpm_manager_testrunner', + 'type': 'executable', + 'includes': ['../common-mk/common_test.gypi'], + 'variables': { + 'deps': [ + 'libchrome-test-<(libbase_ver)', + 'libchromeos-test-<(libbase_ver)', + ], + }, + 'sources': [ + 'client/dbus_proxy_test.cc', + 'common/mock_tpm_manager_interface.cc', + 'server/dbus_service_test.cc', + 'server/mock_local_data_store.cc', + 'server/mock_tpm_initializer.cc', + 'server/mock_tpm_status.cc', + 'server/tpm_manager_service_test.cc', + 'tpm_manager_testrunner.cc', + ], + 'conditions': [ + ['USE_tpm2 == 1', { + 'sources': [ + '../trunks/mock_blob_parser.cc', + '../trunks/mock_hmac_session.cc', + '../trunks/mock_policy_session.cc', + '../trunks/mock_session_manager.cc', + '../trunks/mock_tpm.cc', + '../trunks/mock_tpm_state.cc', + '../trunks/mock_tpm_utility.cc', + '../trunks/trunks_factory_for_test.cc', + 'server/tpm2_status_test.cc', + 'server/tpm2_initializer_test.cc', + ], + }], + ], + 'dependencies': [ + 'libtpm_manager', + 'proto_library', + 'server_library', + ], + }, + ], + }], + ], +} diff --git a/tpm_manager_testrunner.cc b/tpm_manager_testrunner.cc new file mode 100644 index 0000000..3380ea6 --- /dev/null +++ b/tpm_manager_testrunner.cc @@ -0,0 +1,19 @@ +// 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 <base/at_exit.h> +#include <base/command_line.h> +#include <base/logging.h> +#include <chromeos/syslog_logging.h> +#include <gtest/gtest.h> + +int main(int argc, char **argv) { + base::CommandLine::Init(argc, argv); + chromeos::InitLog(chromeos::kLogToStderr); + // Enable verbose logging while running unit tests. + logging::SetMinLogLevel(logging::LOG_VERBOSE); + base::AtExitManager exit_manager; + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} |