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