//
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <memory>
#include <string>
#include <base/command_line.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/memory/ptr_util.h>
#include <base/message_loop/message_loop.h>
#include <brillo/bind_lambda.h>
#if defined(USE_BINDER_IPC)
#include <brillo/binder_watcher.h>
#endif
#include <brillo/daemons/daemon.h>
#include <brillo/syslog_logging.h>
#include <crypto/sha2.h>
#if defined(USE_BINDER_IPC)
#include "tpm_manager/client/tpm_nvram_binder_proxy.h"
#include "tpm_manager/client/tpm_ownership_binder_proxy.h"
#else
#include "tpm_manager/client/tpm_nvram_dbus_proxy.h"
#include "tpm_manager/client/tpm_ownership_dbus_proxy.h"
#endif
#include "tpm_manager/common/print_tpm_manager_proto.h"
#include "tpm_manager/common/tpm_manager.pb.h"
#include "trunks/tpm_generated.h"
namespace tpm_manager {
constexpr char kGetTpmStatusCommand[] = "status";
constexpr char kTakeOwnershipCommand[] = "take_ownership";
constexpr char kRemoveOwnerDependencyCommand[] = "remove_dependency";
constexpr char kDefineSpaceCommand[] = "define_space";
constexpr char kDestroySpaceCommand[] = "destroy_space";
constexpr char kWriteSpaceCommand[] = "write_space";
constexpr char kReadSpaceCommand[] = "read_space";
constexpr char kLockSpaceCommand[] = "lock_space";
constexpr char kListSpacesCommand[] = "list_spaces";
constexpr char kGetSpaceInfoCommand[] = "get_space_info";
constexpr char kDependencySwitch[] = "dependency";
constexpr char kIndexSwitch[] = "index";
constexpr char kSizeSwitch[] = "size";
constexpr char kAttributesSwitch[] = "attributes";
constexpr char kPasswordSwitch[] = "password";
constexpr char kBindToPCR0Switch[] = "bind_to_pcr0";
constexpr char kFileSwitch[] = "file";
constexpr char kUseOwnerSwitch[] = "use_owner_authorization";
constexpr char kLockRead[] = "lock_read";
constexpr char kLockWrite[] = "lock_write";
constexpr char kUsage[] = R"(
Usage: tpm_manager_client <command> [<arguments>]
Commands:
status
Prints TPM status information.
take_ownership
Takes ownership of the Tpm with a random password.
remove_dependency --dependency=<owner_dependency>
Removes the named Tpm owner dependency. E.g. \"Nvram\" or \"Attestation\".
define_space --index=<index> --size=<size> [--attributes=<attribute_list>]
[--password=<password>] [--bind_to_pcr0]
Defines an NV space. The attribute format is a '|' separated list of:
PERSISTENT_WRITE_LOCK: Allow write lock; stay locked until destroyed.
BOOT_WRITE_LOCK: Allow write lock; stay locked until next boot.
BOOT_READ_LOCK: Allow read lock; stay locked until next boot.
WRITE_AUTHORIZATION: Require authorization to write.
READ_AUTHORIZATION: Require authorization to read.
WRITE_EXTEND: Allow only extend operations, not direct writes.
GLOBAL_LOCK: Engage write lock when the global lock is engaged.
PLATFORM_WRITE: Allow write only with 'platform' authorization. This
is similar to the TPM 1.2 'physical presence' notion.
OWNER_WRITE: Allow write only with TPM owner authorization.
OWNER_READ: Allow read only with TPM owner authorization.
This command requires that owner authorization is available. If a password
is given it will be required only as specified by the attributes. E.g. if
READ_AUTHORIZATION is not listed, then the password will not be required
in order to read. Similarly, if the --bind_to_pcr0 option is given, the
current PCR0 value will be required only as specified by the attributes.
destroy_space --index=<index>
Destroys an NV space. This command requires that owner authorization is
available.
write_space --index=<index> --file=<input_file> [--password=<password>]
[--use_owner_authorization]
Writes data from a file to an NV space. Any existing data will be
overwritten.
read_space --index=<index> --file=<output_file> [--password=<password>]
[--use_owner_authorization]
Reads the entire contents of an NV space to a file.
lock_space --index=<index> [--lock_read] [--lock_write]
[--password=<password>] [--use_owner_authorization]
Locks an NV space for read and / or write.
list_spaces
Prints a list of all defined index values.
get_space_info --index=<index>
Prints public information about an NV space.
)";
constexpr char kKnownNVRAMSpaces[] = R"(
NVRAM Index Reference:
TPM 1.2 (32-bit values)
0x00001007 - Chrome OS Firmware Version Rollback Protection
0x00001008 - Chrome OS Kernel Version Rollback Protection
0x00001009 - Chrome OS Firmware Backup
0x0000100A - Chrome OS Firmware Management Parameters
0x20000004 - Chrome OS Install Attributes (aka LockBox)
0x10000001 - Standard TPM_NV_INDEX_DIR (Permanent)
0x1000F000 - Endorsement Certificate (Permanent)
0x30000001 - Endorsement Authority Certificate (Permanent)
0x0000F004 - Standard Test Index (for testing TPM_NV_DefineSpace)
TPM 2.0 (24-bit values)
0x400000 and following - Reserved for Firmware
0x800000 and following - Reserved for Software
0xC00000 and following - Endorsement Certificates
)";
bool ReadFileToString(const std::string& filename, std::string* data) {
return base::ReadFileToString(base::FilePath(filename), data);
}
bool WriteStringToFile(const std::string& data, const std::string& filename) {
int result =
base::WriteFile(base::FilePath(filename), data.data(), data.size());
return (result != -1 && static_cast<size_t>(result) == data.size());
}
uint32_t StringToUint32(const std::string& s) {
return strtoul(s.c_str(), nullptr, 0);
}
uint32_t StringToNvramIndex(const std::string& s) {
return trunks::HR_HANDLE_MASK & StringToUint32(s);
}
using ClientLoopBase = brillo::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;
}
#if defined(USE_BINDER_IPC)
if (!binder_watcher_.Init()) {
LOG(ERROR) << "Error initializing binder watcher.";
return EX_UNAVAILABLE;
}
std::unique_ptr<TpmNvramBinderProxy> nvram_proxy =
base::MakeUnique<TpmNvramBinderProxy>();
std::unique_ptr<TpmOwnershipBinderProxy> ownership_proxy =
base::MakeUnique<TpmOwnershipBinderProxy>();
#else
std::unique_ptr<TpmNvramDBusProxy> nvram_proxy =
base::MakeUnique<TpmNvramDBusProxy>();
std::unique_ptr<TpmOwnershipDBusProxy> ownership_proxy =
base::MakeUnique<TpmOwnershipDBusProxy>();
#endif
if (!nvram_proxy->Initialize()) {
LOG(ERROR) << "Error initializing nvram proxy.";
return EX_UNAVAILABLE;
}
if (!ownership_proxy->Initialize()) {
LOG(ERROR) << "Error initializing ownership proxy.";
return EX_UNAVAILABLE;
}
tpm_nvram_ = std::move(nvram_proxy);
tpm_ownership_ = std::move(ownership_proxy);
exit_code = ScheduleCommand();
if (exit_code == EX_USAGE) {
printf("%s%s", kUsage, kKnownNVRAMSpaces);
}
return exit_code;
}
void OnShutdown(int* exit_code) override {
tpm_nvram_.reset();
tpm_ownership_.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") ||
command_line->GetArgs().size() == 0) {
return EX_USAGE;
}
std::string command = command_line->GetArgs()[0];
if (command == kGetTpmStatusCommand) {
task = base::Bind(&ClientLoop::HandleGetTpmStatus,
weak_factory_.GetWeakPtr());
} else if (command == kTakeOwnershipCommand) {
task = base::Bind(&ClientLoop::HandleTakeOwnership,
weak_factory_.GetWeakPtr());
} else if (command == kRemoveOwnerDependencyCommand) {
if (!command_line->HasSwitch(kDependencySwitch)) {
return EX_USAGE;
}
task = base::Bind(&ClientLoop::HandleRemoveOwnerDependency,
weak_factory_.GetWeakPtr(),
command_line->GetSwitchValueASCII(kDependencySwitch));
} else if (command == kDefineSpaceCommand) {
if (!command_line->HasSwitch(kIndexSwitch) ||
!command_line->HasSwitch(kSizeSwitch)) {
return EX_USAGE;
}
task = base::Bind(
&ClientLoop::HandleDefineSpace, weak_factory_.GetWeakPtr(),
StringToNvramIndex(command_line->GetSwitchValueASCII(kIndexSwitch)),
StringToUint32(command_line->GetSwitchValueASCII(kSizeSwitch)),
command_line->GetSwitchValueASCII(kAttributesSwitch),
command_line->GetSwitchValueASCII(kPasswordSwitch),
command_line->HasSwitch(kBindToPCR0Switch));
} else if (command == kDestroySpaceCommand) {
if (!command_line->HasSwitch(kIndexSwitch)) {
return EX_USAGE;
}
task = base::Bind(
&ClientLoop::HandleDestroySpace, weak_factory_.GetWeakPtr(),
StringToNvramIndex(command_line->GetSwitchValueASCII(kIndexSwitch)));
} else if (command == kWriteSpaceCommand) {
if (!command_line->HasSwitch(kIndexSwitch) ||
!command_line->HasSwitch(kFileSwitch)) {
return EX_USAGE;
}
task = base::Bind(
&ClientLoop::HandleWriteSpace, weak_factory_.GetWeakPtr(),
StringToNvramIndex(command_line->GetSwitchValueASCII(kIndexSwitch)),
command_line->GetSwitchValueASCII(kFileSwitch),
command_line->GetSwitchValueASCII(kPasswordSwitch),
command_line->HasSwitch(kUseOwnerSwitch));
} else if (command == kReadSpaceCommand) {
if (!command_line->HasSwitch(kIndexSwitch) ||
!command_line->HasSwitch(kFileSwitch)) {
return EX_USAGE;
}
task = base::Bind(
&ClientLoop::HandleReadSpace, weak_factory_.GetWeakPtr(),
StringToNvramIndex(command_line->GetSwitchValueASCII(kIndexSwitch)),
command_line->GetSwitchValueASCII(kFileSwitch),
command_line->GetSwitchValueASCII(kPasswordSwitch),
command_line->HasSwitch(kUseOwnerSwitch));
} else if (command == kLockSpaceCommand) {
if (!command_line->HasSwitch(kIndexSwitch)) {
return EX_USAGE;
}
task = base::Bind(
&ClientLoop::HandleLockSpace, weak_factory_.GetWeakPtr(),
StringToNvramIndex(command_line->GetSwitchValueASCII(kIndexSwitch)),
command_line->HasSwitch(kLockRead),
command_line->HasSwitch(kLockWrite),
command_line->GetSwitchValueASCII(kPasswordSwitch),
command_line->HasSwitch(kUseOwnerSwitch));
} else if (command == kListSpacesCommand) {
task =
base::Bind(&ClientLoop::HandleListSpaces, weak_factory_.GetWeakPtr());
} else if (command == kGetSpaceInfoCommand) {
if (!command_line->HasSwitch(kIndexSwitch)) {
return EX_USAGE;
}
task = base::Bind(
&ClientLoop::HandleGetSpaceInfo, weak_factory_.GetWeakPtr(),
StringToNvramIndex(command_line->GetSwitchValueASCII(kIndexSwitch)));
} else {
// Command line arguments did not match any valid commands.
return EX_USAGE;
}
base::MessageLoop::current()->PostTask(FROM_HERE, task);
return EX_OK;
}
// Template to print reply protobuf.
template <typename ProtobufType>
void PrintReplyAndQuit(const ProtobufType& reply) {
LOG(INFO) << "Message Reply: " << GetProtoDebugString(reply);
Quit();
}
void HandleGetTpmStatus() {
GetTpmStatusRequest request;
tpm_ownership_->GetTpmStatus(
request, base::Bind(&ClientLoop::PrintReplyAndQuit<GetTpmStatusReply>,
weak_factory_.GetWeakPtr()));
}
void HandleTakeOwnership() {
TakeOwnershipRequest request;
tpm_ownership_->TakeOwnership(
request, base::Bind(&ClientLoop::PrintReplyAndQuit<TakeOwnershipReply>,
weak_factory_.GetWeakPtr()));
}
void HandleRemoveOwnerDependency(const std::string& owner_dependency) {
RemoveOwnerDependencyRequest request;
request.set_owner_dependency(owner_dependency);
tpm_ownership_->RemoveOwnerDependency(
request,
base::Bind(&ClientLoop::PrintReplyAndQuit<RemoveOwnerDependencyReply>,
weak_factory_.GetWeakPtr()));
}
bool DecodeAttribute(const std::string& attribute_str,
NvramSpaceAttribute* attribute) {
if (attribute_str == "PERSISTENT_WRITE_LOCK") {
*attribute = NVRAM_PERSISTENT_WRITE_LOCK;
return true;
}
if (attribute_str == "BOOT_WRITE_LOCK") {
*attribute = NVRAM_BOOT_WRITE_LOCK;
return true;
}
if (attribute_str == "BOOT_READ_LOCK") {
*attribute = NVRAM_BOOT_READ_LOCK;
return true;
}
if (attribute_str == "WRITE_AUTHORIZATION") {
*attribute = NVRAM_WRITE_AUTHORIZATION;
return true;
}
if (attribute_str == "READ_AUTHORIZATION") {
*attribute = NVRAM_READ_AUTHORIZATION;
return true;
}
if (attribute_str == "WRITE_EXTEND") {
*attribute = NVRAM_WRITE_EXTEND;
return true;
}
if (attribute_str == "GLOBAL_LOCK") {
*attribute = NVRAM_GLOBAL_LOCK;
return true;
}
if (attribute_str == "PLATFORM_WRITE") {
*attribute = NVRAM_PLATFORM_WRITE;
return true;
}
if (attribute_str == "OWNER_WRITE") {
*attribute = NVRAM_OWNER_WRITE;
return true;
}
if (attribute_str == "OWNER_READ") {
*attribute = NVRAM_OWNER_READ;
return true;
}
LOG(ERROR) << "Unrecognized attribute: " << attribute_str;
return false;
}
void HandleDefineSpace(uint32_t index,
size_t size,
const std::string& attributes,
const std::string& password,
bool bind_to_pcr0) {
DefineSpaceRequest request;
request.set_index(index);
request.set_size(size);
std::string::size_type pos = 0;
std::string::size_type next_pos = 0;
while (next_pos != std::string::npos) {
next_pos = attributes.find('|', pos);
std::string attribute_str;
if (next_pos == std::string::npos) {
attribute_str = attributes.substr(pos);
} else {
attribute_str = attributes.substr(pos, next_pos - pos);
}
if (!attribute_str.empty()) {
NvramSpaceAttribute attribute;
if (!DecodeAttribute(attribute_str, &attribute)) {
Quit();
return;
}
request.add_attributes(attribute);
}
pos = next_pos + 1;
}
request.set_authorization_value(crypto::SHA256HashString(password));
request.set_policy(bind_to_pcr0 ? NVRAM_POLICY_PCR0 : NVRAM_POLICY_NONE);
tpm_nvram_->DefineSpace(
request, base::Bind(&ClientLoop::PrintReplyAndQuit<DefineSpaceReply>,
weak_factory_.GetWeakPtr()));
}
void HandleDestroySpace(uint32_t index) {
DestroySpaceRequest request;
request.set_index(index);
tpm_nvram_->DestroySpace(
request, base::Bind(&ClientLoop::PrintReplyAndQuit<DestroySpaceReply>,
weak_factory_.GetWeakPtr()));
}
void HandleWriteSpace(uint32_t index,
const std::string& input_file,
const std::string& password,
bool use_owner_authorization) {
WriteSpaceRequest request;
request.set_index(index);
std::string data;
if (!ReadFileToString(input_file, &data)) {
LOG(ERROR) << "Failed to read input file.";
Quit();
return;
}
request.set_data(data);
request.set_authorization_value(crypto::SHA256HashString(password));
request.set_use_owner_authorization(use_owner_authorization);
tpm_nvram_->WriteSpace(
request, base::Bind(&ClientLoop::PrintReplyAndQuit<WriteSpaceReply>,
weak_factory_.GetWeakPtr()));
}
void HandleReadSpaceReply(const std::string& output_file,
const ReadSpaceReply& reply) {
if (!WriteStringToFile(reply.data(), output_file)) {
LOG(ERROR) << "Failed to write output file.";
}
LOG(INFO) << "Message Reply: " << GetProtoDebugString(reply);
Quit();
}
void HandleReadSpace(uint32_t index,
const std::string& output_file,
const std::string& password,
bool use_owner_authorization) {
ReadSpaceRequest request;
request.set_index(index);
request.set_authorization_value(crypto::SHA256HashString(password));
request.set_use_owner_authorization(use_owner_authorization);
tpm_nvram_->ReadSpace(request,
base::Bind(&ClientLoop::HandleReadSpaceReply,
weak_factory_.GetWeakPtr(), output_file));
}
void HandleLockSpace(uint32_t index,
bool lock_read,
bool lock_write,
const std::string& password,
bool use_owner_authorization) {
LockSpaceRequest request;
request.set_index(index);
request.set_lock_read(lock_read);
request.set_lock_write(lock_write);
request.set_authorization_value(crypto::SHA256HashString(password));
request.set_use_owner_authorization(use_owner_authorization);
tpm_nvram_->LockSpace(
request, base::Bind(&ClientLoop::PrintReplyAndQuit<LockSpaceReply>,
weak_factory_.GetWeakPtr()));
}
void HandleListSpaces() {
printf("%s\n", kKnownNVRAMSpaces);
ListSpacesRequest request;
tpm_nvram_->ListSpaces(
request, base::Bind(&ClientLoop::PrintReplyAndQuit<ListSpacesReply>,
weak_factory_.GetWeakPtr()));
}
void HandleGetSpaceInfo(uint32_t index) {
GetSpaceInfoRequest request;
request.set_index(index);
tpm_nvram_->GetSpaceInfo(
request, base::Bind(&ClientLoop::PrintReplyAndQuit<GetSpaceInfoReply>,
weak_factory_.GetWeakPtr()));
}
// IPC proxy interfaces.
std::unique_ptr<tpm_manager::TpmNvramInterface> tpm_nvram_;
std::unique_ptr<tpm_manager::TpmOwnershipInterface> tpm_ownership_;
#if defined(USE_BINDER_IPC)
brillo::BinderWatcher binder_watcher_;
#endif
// 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);
brillo::InitLog(brillo::kLogToStderr);
tpm_manager::ClientLoop loop;
return loop.Run();
}