blob: 15ff24acb3354dec5aca0fde67e4298eb46e7747 [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>
Yi Chou9d24b462020-12-04 01:12:57 +080011#include <base/logging.h>
12#include <base/posix/eintr_wrapper.h>
Yi Chou9d24b462020-12-04 01:12:57 +080013#include <fcntl.h>
Yi Choudee22a52020-12-07 15:06:22 +080014#include <libminijail.h>
Yi Chou9d24b462020-12-04 01:12:57 +080015#include <linux/vtpm_proxy.h>
Yi Choudee22a52020-12-07 15:06:22 +080016#include <scoped_minijail.h>
17#include <signal.h>
Yi Chou9d24b462020-12-04 01:12:57 +080018#include <sys/ioctl.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <sysexits.h>
22#include <tpm2/tpm_simulator.hpp>
23#include <unistd.h>
24
25#include "tpm2-simulator/simulator.h"
26
27namespace {
Yi Choudee22a52020-12-07 15:06:22 +080028constexpr char kSimulatorUser[] = "tpm2-simulator";
29constexpr char kSimulatorGroup[] = "tpm2-simulator";
30constexpr char kSimulatorSeccompPath[] =
31 "/usr/share/policy/tpm2-simulator.policy";
Yi Chou9d24b462020-12-04 01:12:57 +080032constexpr char kVtpmxPath[] = "/dev/vtpmx";
Yi Choudee22a52020-12-07 15:06:22 +080033constexpr char kDevTpmPathPrefix[] = "/dev/tpm";
Yi Chou9d24b462020-12-04 01:12:57 +080034constexpr size_t kMaxCommandSize = MAX_COMMAND_SIZE;
35constexpr size_t kHeaderSize = 10;
Yi Chou9d24b462020-12-04 01:12:57 +080036
Yi Choudee22a52020-12-07 15:06:22 +080037base::ScopedFD RegisterVTPM(base::FilePath* tpm_path) {
Yi Chou9d24b462020-12-04 01:12:57 +080038 struct vtpm_proxy_new_dev new_dev = {};
39 new_dev.flags = VTPM_PROXY_FLAG_TPM2;
40 base::ScopedFD vtpmx_fd(HANDLE_EINTR(open(kVtpmxPath, O_RDWR | O_CLOEXEC)));
41 if (!vtpmx_fd.is_valid()) {
42 return vtpmx_fd;
43 }
44 if (ioctl(vtpmx_fd.get(), VTPM_PROXY_IOC_NEW_DEV, &new_dev) < 0) {
45 PLOG(ERROR) << "Create vTPM failed.";
46 // return an invalid FD.
47 return {};
48 }
Yi Choudee22a52020-12-07 15:06:22 +080049 *tpm_path =
50 base::FilePath(kDevTpmPathPrefix + std::to_string(new_dev.tpm_num));
Yi Chou9d24b462020-12-04 01:12:57 +080051 LOG(INFO) << "Create TPM at: /dev/tpm" << new_dev.tpm_num;
52 return base::ScopedFD(new_dev.fd);
53}
54
55void InitializeVTPM() {
56 // Initialize TPM.
57 tpm2::_plat__Signal_PowerOn();
58 /*
59 * Make sure NV RAM metadata is initialized, needed to check
60 * manufactured status. This is a speculative call which will have to
61 * be repeated in case the TPM has not been through the manufacturing
62 * sequence yet. No harm in calling it twice in that case.
63 */
64 tpm2::_TPM_Init();
65 tpm2::_plat__SetNvAvail();
66
67 if (!tpm2::tpm_manufactured()) {
68 tpm2::TPM_Manufacture(true);
69 // TODO(b/132145000): Verify if the second call to _TPM_Init() is necessary.
70 tpm2::_TPM_Init();
71 if (!tpm2::tpm_endorse())
72 LOG(ERROR) << __func__ << " Failed to endorse TPM with a fixed key.";
73 }
Yi Chou9d24b462020-12-04 01:12:57 +080074}
75
76std::string CommandWithCode(uint32_t code) {
77 std::string response;
78 response.resize(10);
79 unsigned char* buffer = reinterpret_cast<unsigned char*>(response.data());
80 tpm2::TPM_ST tag = TPM_ST_NO_SESSIONS;
81 tpm2::INT32 size = 10;
82 tpm2::UINT32 len = size;
83 tpm2::TPMI_ST_COMMAND_TAG_Marshal(&tag, &buffer, &size);
84 tpm2::UINT32_Marshal(&len, &buffer, &size);
85 tpm2::TPM_CC_Marshal(&code, &buffer, &size);
86 return response;
87}
88
89unsigned int GetCommandSize(const std::string& command) {
90 unsigned char* header =
91 reinterpret_cast<unsigned char*>(const_cast<char*>(command.data()));
92 int32_t header_size = command.size();
93 tpm2::TPMI_ST_COMMAND_TAG tag;
94 uint32_t command_size;
95 tpm2::TPM_RC rc =
96 tpm2::TPMI_ST_COMMAND_TAG_Unmarshal(&tag, &header, &header_size);
97 if (rc != TPM_RC_SUCCESS) {
98 LOG(ERROR) << "Failed to parse tag";
99 return command.size();
100 }
101 rc = tpm2::UINT32_Unmarshal(&command_size, &header, &header_size);
102 if (rc != TPM_RC_SUCCESS) {
103 LOG(ERROR) << "Failed to parse size";
104 return command.size();
105 }
106 return command_size;
107}
108
109std::string RunCommand(const std::string& command) {
110 // TODO(yich): ExecuteCommand would mutate the command buffer, so we created a
111 // copy of the input command at here.
112 std::string command_copy = command;
113 unsigned char* command_ptr =
114 reinterpret_cast<unsigned char*>(command_copy.data());
115 unsigned char* header = command_ptr;
116 int32_t header_size = command.size();
117 tpm2::TPMI_ST_COMMAND_TAG tag;
118 uint32_t command_size;
119 tpm2::TPM_CC command_code = 0;
120 tpm2::TPM_RC rc =
121 tpm2::TPMI_ST_COMMAND_TAG_Unmarshal(&tag, &header, &header_size);
Yi Choudee22a52020-12-07 15:06:22 +0800122 if (rc != TPM_RC_SUCCESS) {
123 return CommandWithCode(rc);
124 }
Yi Chou9d24b462020-12-04 01:12:57 +0800125 rc = tpm2::UINT32_Unmarshal(&command_size, &header, &header_size);
Yi Choudee22a52020-12-07 15:06:22 +0800126 if (rc != TPM_RC_SUCCESS) {
127 return CommandWithCode(rc);
128 }
Yi Chou9d24b462020-12-04 01:12:57 +0800129 rc = tpm2::TPM_CC_Unmarshal(&command_code, &header, &header_size);
130 if (command_code == TPM2_CC_SET_LOCALITY) {
131 return CommandWithCode(TPM_RC_SUCCESS);
132 }
133
134 unsigned int response_size;
135 unsigned char* response;
136 tpm2::ExecuteCommand(command.size(), command_ptr, &response_size, &response);
137 return std::string(reinterpret_cast<char*>(response), response_size);
138}
139
Yi Choudee22a52020-12-07 15:06:22 +0800140void InitMinijailSandbox() {
141 ScopedMinijail j(minijail_new());
142 minijail_no_new_privs(j.get());
143 minijail_log_seccomp_filter_failures(j.get());
144 minijail_parse_seccomp_filters(j.get(), kSimulatorSeccompPath);
145 minijail_use_seccomp_filter(j.get());
146 minijail_change_user(j.get(), kSimulatorUser);
147 minijail_change_group(j.get(), kSimulatorGroup);
148 minijail_inherit_usergroups(j.get());
149 minijail_enter(j.get());
150}
151
Yi Chou9d24b462020-12-04 01:12:57 +0800152} // namespace
153
154namespace tpm2_simulator {
155
156int SimulatorDaemon::OnInit() {
157 int exit_code = Daemon::OnInit();
158 if (exit_code != EX_OK)
159 return exit_code;
160 InitializeVTPM();
Yi Choudee22a52020-12-07 15:06:22 +0800161 base::FilePath tpm_path;
162 command_fd_ = RegisterVTPM(&tpm_path);
Yi Chou9d24b462020-12-04 01:12:57 +0800163 if (!command_fd_.is_valid()) {
164 LOG(ERROR) << "Failed to register vTPM";
165 return EX_OSERR;
166 }
167 command_fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(
168 command_fd_.get(),
169 base::BindRepeating(&SimulatorDaemon::OnCommand, base::Unretained(this)));
Yi Choudee22a52020-12-07 15:06:22 +0800170 tpm_watcher_.reset(new base::FilePathWatcher);
171 tpm_watcher_->Watch(
172 tpm_path, false,
173 base::Bind(&SimulatorDaemon::OnTpmPathChange, base::Unretained(this)));
Yi Chou9d24b462020-12-04 01:12:57 +0800174 return EX_OK;
175}
176
177void SimulatorDaemon::OnCommand() {
178 char buffer[kMaxCommandSize];
179 do {
180 std::string request;
181 remain_request_.swap(request);
182
183 // Read request header.
184 while (kHeaderSize > request.size()) {
185 ssize_t size =
186 HANDLE_EINTR(read(command_fd_.get(), buffer, kMaxCommandSize));
187 CHECK_GE(size, 0);
188 request.append(buffer, size);
189 }
190
191 const uint32_t command_size = GetCommandSize(request);
192
193 // Read request body.
194 while (command_size > request.size()) {
195 ssize_t size =
196 HANDLE_EINTR(read(command_fd_.get(), buffer, kMaxCommandSize));
197 CHECK_GE(size, 0);
198 request.append(buffer, size);
199 }
200
201 // Trim request.
202 if (command_size < request.size()) {
203 remain_request_ = request.substr(command_size);
204 request.resize(command_size);
205 }
206
207 // Run command.
208 std::string response = RunCommand(request);
209
210 // Write response.
211 if (!base::WriteFileDescriptor(command_fd_.get(), response.c_str(),
212 response.size())) {
213 PLOG(ERROR) << "WriteFileDescriptor failed.";
214 }
215 } while (!remain_request_.empty());
216}
217
Yi Choudee22a52020-12-07 15:06:22 +0800218void SimulatorDaemon::OnTpmPathChange(const base::FilePath& path, bool error) {
219 if (error) {
220 LOG(ERROR) << "Got error while hearing about change to " << path.value();
221 return;
222 }
223 if (!initialized_ && base::PathExists(path)) {
224 LOG(INFO) << "vTPM initialized: " << path.value();
225 tpm_watcher_.reset();
226 initialized_ = true;
227 if (sigstop_on_initialized_) {
228 // Raise the SIGSTOP, so upstart would know the initialization process had
229 // been finished.
230 raise(SIGSTOP);
231 }
232 // Initialize the minijail.
233 InitMinijailSandbox();
234 }
235}
236
Yi Chou9d24b462020-12-04 01:12:57 +0800237} // namespace tpm2_simulator