blob: 9f0b20dba72be99ba39283e2d358c10ffaeeef1f [file] [log] [blame]
Yi Chou9d24b462020-12-04 01:12:57 +08001// Copyright 2021 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <string>
6#include <utility>
7
8#include <base/callback.h>
9#include <base/files/file_util.h>
10#include <base/files/file.h>
11#include <base/hash/sha1.h>
12#include <base/logging.h>
13#include <base/posix/eintr_wrapper.h>
14#include <crypto/sha2.h>
15#include <fcntl.h>
16#include <linux/vtpm_proxy.h>
17#include <sys/ioctl.h>
18#include <sys/stat.h>
19#include <sys/types.h>
20#include <sysexits.h>
21#include <tpm2/tpm_simulator.hpp>
22#include <unistd.h>
23
24#include "tpm2-simulator/simulator.h"
25
26namespace {
27
28constexpr char kVtpmxPath[] = "/dev/vtpmx";
29constexpr size_t kMaxCommandSize = MAX_COMMAND_SIZE;
30constexpr size_t kHeaderSize = 10;
31constexpr unsigned char kStartupCmd[] = {
32 0x80, 0x01, /* TPM_ST_NO_SESSIONS */
33 0x00, 0x00, 0x00, 0x0c, /* commandSize = 12 */
34 0x00, 0x00, 0x01, 0x44, /* TPM_CC_Startup */
35 0x00, 0x00 /* TPM_SU_CLEAR */
36};
37
38// Resizes extend_data to size crypto::kSHA256Length and uses the result to
39// extend the indicated PCR.
40void ExtendPcr(unsigned int pcr_index, const std::string& extend_data) {
41 std::string mode_digest = extend_data;
42 mode_digest.resize(crypto::kSHA256Length);
43 tpm2::extend_pcr(pcr_index, mode_digest.data());
44}
45
46// According to the specified boot mode, extends PCR0 as cr50 does.
47// It should only be called once after the PCR0 value is set to all 0s
48// (e.g. running Startup with Clear). Calling it twice without resetting the PCR
49// will leave the TPM in an unknown boot mode.
50// - developer_mode: 1 if in developer mode, 0 otherwise,
51// - recovery_mode: 1 if in recovery mode, 0 otherwise,
52// - verified_firmware: 1 if verified firmware, 0 if developer firmware.
53void ExtendPcr0BootMode(const char developer_mode,
54 const char recovery_mode,
55 const char verified_firmware) {
56 const std::string mode({developer_mode, recovery_mode, verified_firmware});
57 ExtendPcr(/*pcr_index=*/0, base::SHA1HashString(mode));
58}
59
60base::ScopedFD RegisterVTPM() {
61 struct vtpm_proxy_new_dev new_dev = {};
62 new_dev.flags = VTPM_PROXY_FLAG_TPM2;
63 base::ScopedFD vtpmx_fd(HANDLE_EINTR(open(kVtpmxPath, O_RDWR | O_CLOEXEC)));
64 if (!vtpmx_fd.is_valid()) {
65 return vtpmx_fd;
66 }
67 if (ioctl(vtpmx_fd.get(), VTPM_PROXY_IOC_NEW_DEV, &new_dev) < 0) {
68 PLOG(ERROR) << "Create vTPM failed.";
69 // return an invalid FD.
70 return {};
71 }
72 LOG(INFO) << "Create TPM at: /dev/tpm" << new_dev.tpm_num;
73 return base::ScopedFD(new_dev.fd);
74}
75
76void InitializeVTPM() {
77 // Initialize TPM.
78 tpm2::_plat__Signal_PowerOn();
79 /*
80 * Make sure NV RAM metadata is initialized, needed to check
81 * manufactured status. This is a speculative call which will have to
82 * be repeated in case the TPM has not been through the manufacturing
83 * sequence yet. No harm in calling it twice in that case.
84 */
85 tpm2::_TPM_Init();
86 tpm2::_plat__SetNvAvail();
87
88 if (!tpm2::tpm_manufactured()) {
89 tpm2::TPM_Manufacture(true);
90 // TODO(b/132145000): Verify if the second call to _TPM_Init() is necessary.
91 tpm2::_TPM_Init();
92 if (!tpm2::tpm_endorse())
93 LOG(ERROR) << __func__ << " Failed to endorse TPM with a fixed key.";
94 }
95
96 // Send TPM2_Startup(TPM_SU_CLEAR), ignore the result. This is normally done
97 // by firmware. Without TPM2_Startup, TpmUtility::CheckState() fails,
98 // ResourceManager aborts initialization, and trunks daemon dies.
99 unsigned int response_size;
100 unsigned char* response;
101 unsigned char startup_cmd[sizeof(kStartupCmd)];
102 memcpy(startup_cmd, kStartupCmd, sizeof(kStartupCmd));
103
104 // TODO(yich): ExecuteCommand would mutate the command buffer, so we can't
105 // mark the command buffer const here.
106 tpm2::ExecuteCommand(sizeof(startup_cmd), startup_cmd, &response_size,
107 &response);
108 LOG(INFO) << "TPM2_Startup(TPM_SU_CLEAR) sent.";
109
110 ExtendPcr0BootMode(/*developer_mode=*/1, /*recovery_mode=*/0,
111 /*verified_firmware=*/0);
112 // Assign an arbitrary value to PCR1.
113 ExtendPcr(/*pcr_index=*/1, /*extend_data=*/"PCR1");
114}
115
116std::string CommandWithCode(uint32_t code) {
117 std::string response;
118 response.resize(10);
119 unsigned char* buffer = reinterpret_cast<unsigned char*>(response.data());
120 tpm2::TPM_ST tag = TPM_ST_NO_SESSIONS;
121 tpm2::INT32 size = 10;
122 tpm2::UINT32 len = size;
123 tpm2::TPMI_ST_COMMAND_TAG_Marshal(&tag, &buffer, &size);
124 tpm2::UINT32_Marshal(&len, &buffer, &size);
125 tpm2::TPM_CC_Marshal(&code, &buffer, &size);
126 return response;
127}
128
129unsigned int GetCommandSize(const std::string& command) {
130 unsigned char* header =
131 reinterpret_cast<unsigned char*>(const_cast<char*>(command.data()));
132 int32_t header_size = command.size();
133 tpm2::TPMI_ST_COMMAND_TAG tag;
134 uint32_t command_size;
135 tpm2::TPM_RC rc =
136 tpm2::TPMI_ST_COMMAND_TAG_Unmarshal(&tag, &header, &header_size);
137 if (rc != TPM_RC_SUCCESS) {
138 LOG(ERROR) << "Failed to parse tag";
139 return command.size();
140 }
141 rc = tpm2::UINT32_Unmarshal(&command_size, &header, &header_size);
142 if (rc != TPM_RC_SUCCESS) {
143 LOG(ERROR) << "Failed to parse size";
144 return command.size();
145 }
146 return command_size;
147}
148
149std::string RunCommand(const std::string& command) {
150 // TODO(yich): ExecuteCommand would mutate the command buffer, so we created a
151 // copy of the input command at here.
152 std::string command_copy = command;
153 unsigned char* command_ptr =
154 reinterpret_cast<unsigned char*>(command_copy.data());
155 unsigned char* header = command_ptr;
156 int32_t header_size = command.size();
157 tpm2::TPMI_ST_COMMAND_TAG tag;
158 uint32_t command_size;
159 tpm2::TPM_CC command_code = 0;
160 tpm2::TPM_RC rc =
161 tpm2::TPMI_ST_COMMAND_TAG_Unmarshal(&tag, &header, &header_size);
162 CHECK_EQ(rc, TPM_RC_SUCCESS);
163 rc = tpm2::UINT32_Unmarshal(&command_size, &header, &header_size);
164 CHECK_EQ(rc, TPM_RC_SUCCESS);
165 rc = tpm2::TPM_CC_Unmarshal(&command_code, &header, &header_size);
166 if (command_code == TPM2_CC_SET_LOCALITY) {
167 return CommandWithCode(TPM_RC_SUCCESS);
168 }
169
170 unsigned int response_size;
171 unsigned char* response;
172 tpm2::ExecuteCommand(command.size(), command_ptr, &response_size, &response);
173 return std::string(reinterpret_cast<char*>(response), response_size);
174}
175
176} // namespace
177
178namespace tpm2_simulator {
179
180int SimulatorDaemon::OnInit() {
181 int exit_code = Daemon::OnInit();
182 if (exit_code != EX_OK)
183 return exit_code;
184 InitializeVTPM();
185 command_fd_ = RegisterVTPM();
186 if (!command_fd_.is_valid()) {
187 LOG(ERROR) << "Failed to register vTPM";
188 return EX_OSERR;
189 }
190 command_fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(
191 command_fd_.get(),
192 base::BindRepeating(&SimulatorDaemon::OnCommand, base::Unretained(this)));
193 return EX_OK;
194}
195
196void SimulatorDaemon::OnCommand() {
197 char buffer[kMaxCommandSize];
198 do {
199 std::string request;
200 remain_request_.swap(request);
201
202 // Read request header.
203 while (kHeaderSize > request.size()) {
204 ssize_t size =
205 HANDLE_EINTR(read(command_fd_.get(), buffer, kMaxCommandSize));
206 CHECK_GE(size, 0);
207 request.append(buffer, size);
208 }
209
210 const uint32_t command_size = GetCommandSize(request);
211
212 // Read request body.
213 while (command_size > request.size()) {
214 ssize_t size =
215 HANDLE_EINTR(read(command_fd_.get(), buffer, kMaxCommandSize));
216 CHECK_GE(size, 0);
217 request.append(buffer, size);
218 }
219
220 // Trim request.
221 if (command_size < request.size()) {
222 remain_request_ = request.substr(command_size);
223 request.resize(command_size);
224 }
225
226 // Run command.
227 std::string response = RunCommand(request);
228
229 // Write response.
230 if (!base::WriteFileDescriptor(command_fd_.get(), response.c_str(),
231 response.size())) {
232 PLOG(ERROR) << "WriteFileDescriptor failed.";
233 }
234 } while (!remain_request_.empty());
235}
236
237} // namespace tpm2_simulator