514 lines
19 KiB
C++
514 lines
19 KiB
C++
//
|
|
// 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();
|
|
}
|