1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "vtpm_manager"
18
19 #include <linux/types.h>
20 #include <linux/vtpm_proxy.h>
21 #include <sys/sysmacros.h>
22 #include <tss2/tss2_rc.h>
23
24 #include <future>
25
26 #include <gflags/gflags.h>
27 #include <android-base/endian.h>
28 #include <android-base/logging.h>
29
30 #include "common/libs/fs/shared_buf.h"
31 #include "common/libs/fs/shared_fd.h"
32 #include "guest/commands/vtpm_manager/commands.h"
33
34 DEFINE_uint32(tpm_vsock_port, 0, "vsock port to connect to for the TPM");
35
36 struct __attribute__((__packed__)) tpm_message_header {
37 __be16 tag;
38 __be32 length;
39 __be32 ordinal;
40 };
41
42 unsigned char locality = 0;
43
ReadResponseLoop(cuttlefish::SharedFD in_fd,cuttlefish::SharedFD out_fd)44 bool ReadResponseLoop(cuttlefish::SharedFD in_fd, cuttlefish::SharedFD out_fd) {
45 std::vector<char> message;
46 while (true) {
47 std::uint32_t response_size;
48 CHECK(cuttlefish::ReadExactBinary(in_fd, &response_size) == 4)
49 << "Could not read response size";
50 // the tpm simulator writes 4 extra bytes at the end of the message.
51 response_size = be32toh(response_size);
52 message.resize(response_size, '\0');
53 CHECK(cuttlefish::ReadExact(in_fd, &message) == response_size)
54 << "Could not read response message";
55 auto header = reinterpret_cast<tpm_message_header*>(message.data());
56 auto host_rc = betoh32(header->ordinal);
57 LOG(DEBUG) << "TPM response was: \"" << Tss2_RC_Decode(host_rc) << "\" ("
58 << host_rc << ")";
59 std::vector<char> response_bytes(4, 0);
60 CHECK(cuttlefish::ReadExact(in_fd, &response_bytes) == 4)
61 << "Could not read parity response";
62 CHECK(cuttlefish::WriteAll(out_fd, message) == message.size())
63 << "Could not forward message to vTPM";
64 }
65 }
66
SendCommand(cuttlefish::SharedFD out_fd,std::vector<char> command)67 void SendCommand(cuttlefish::SharedFD out_fd, std::vector<char> command) {
68 // TODO(schuffelen): Implement this logic on the host.
69 // TPM2 simulator command protocol.
70 std::uint32_t command_num = htobe32(8); // TPM_SEND_COMMAND
71 CHECK(cuttlefish::WriteAllBinary(out_fd, &command_num) == 4)
72 << "Could not send TPM_SEND_COMMAND";
73 CHECK(cuttlefish::WriteAllBinary(out_fd, (char*)&locality) == 1)
74 << "Could not send locality";
75 std::uint32_t length = htobe32(command.size());
76 CHECK(cuttlefish::WriteAllBinary(out_fd, &length) == 4)
77 << "Could not send command length";
78 CHECK(cuttlefish::WriteAll(out_fd, command) == command.size())
79 << "Could not write TPM message";
80 }
81
SendCommandLoop(cuttlefish::SharedFD in_fd,cuttlefish::SharedFD out_fd)82 bool SendCommandLoop(cuttlefish::SharedFD in_fd, cuttlefish::SharedFD out_fd) {
83 std::vector<char> message(8192, '\0');
84 while (true) {
85 std::int32_t data_length = 0;
86 // Read the whole message in one chunk. The kernel returns EIO if the buffer
87 // is not large enough.
88 // https://github.com/torvalds/linux/blob/407e9ef72476e64937ebec44cc835e03a25fb408/drivers/char/tpm/tpm_vtpm_proxy.c#L98
89 while ((data_length = in_fd->Read(message.data(), message.size())) < 0) {
90 CHECK(in_fd->GetErrno() == EIO) << "Error in reading TPM command from "
91 << "kernel: " << in_fd->StrError();
92 message.resize((message.size() + 1) * 2, '\0');
93 }
94 message.resize(data_length, 0);
95 auto header = reinterpret_cast<tpm_message_header*>(message.data());
96 LOG(DEBUG) << "Received TPM command "
97 << TpmCommandName(betoh32(header->ordinal));
98 if (header->ordinal == htobe32(TPM2_CC_SET_LOCALITY)) { // "Driver command"
99 locality = *reinterpret_cast<unsigned char*>(header + 1);
100 header->ordinal = htobe32(locality);
101 header->length = htobe32(sizeof(tpm_message_header));
102 message.resize(sizeof(tpm_message_header), '\0');
103 CHECK(cuttlefish::WriteAll(in_fd, message) == message.size())
104 << "Could not write TPM message";
105 } else {
106 SendCommand(out_fd, message);
107 }
108 }
109 return false;
110 }
111
main(int argc,char ** argv)112 int main(int argc, char** argv) {
113 setenv("ANDROID_LOG_TAGS", "*:v", 1);
114 ::android::base::InitLogging(argv);
115 gflags::ParseCommandLineFlags(&argc, &argv, true);
116
117 CHECK(FLAGS_tpm_vsock_port != 0) << "Need a value for -tpm_vsock_port";
118
119 auto proxy = cuttlefish::SharedFD::VsockClient(2, FLAGS_tpm_vsock_port, SOCK_STREAM);
120 CHECK(proxy->IsOpen()) << proxy->StrError();
121
122 auto vtpmx = cuttlefish::SharedFD::Open("/dev/vtpmx", O_RDWR | O_CLOEXEC);
123 CHECK(vtpmx->IsOpen()) << vtpmx->StrError();
124
125 vtpm_proxy_new_dev vtpm_creation;
126 vtpm_creation.flags = VTPM_PROXY_FLAG_TPM2;
127
128 CHECK(vtpmx->Ioctl(VTPM_PROXY_IOC_NEW_DEV, &vtpm_creation) == 0) << vtpmx->StrError();
129
130 auto device_fd = cuttlefish::SharedFD::Dup(vtpm_creation.fd);
131 CHECK(device_fd->IsOpen()) << device_fd->StrError();
132 close(vtpm_creation.fd);
133
134 LOG(VERBOSE) << "major was " << vtpm_creation.major << " minor was " << vtpm_creation.minor;
135
136 auto proxy_to_device = std::async(std::launch::async, ReadResponseLoop, proxy, device_fd);
137 auto device_to_proxy = std::async(std::launch::async, SendCommandLoop, device_fd, proxy);
138
139 CHECK(proxy_to_device.get())
140 << "(" << device_fd->StrError() << ")"
141 << "(" << proxy->StrError() << ")";
142 CHECK(device_to_proxy.get())
143 << "(" << device_fd->StrError() << ")"
144 << "(" << proxy->StrError() << ")";
145 }
146
147