// // 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(); }