xref: /qemu/backends/tpm/tpm_util.c (revision d64db833d6e3cbe9ea5f36342480f920f3675cea)
1 /*
2  * TPM utility functions
3  *
4  *  Copyright (c) 2010 - 2015 IBM Corporation
5  *  Authors:
6  *    Stefan Berger <stefanb@us.ibm.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, see <http://www.gnu.org/licenses/>
20  */
21 
22 #include "qemu/osdep.h"
23 #include "qemu/error-report.h"
24 #include "qemu/cutils.h"
25 #include "qapi/error.h"
26 #include "qapi/visitor.h"
27 #include "tpm_int.h"
28 #include "system/memory.h"
29 #include "hw/qdev-properties.h"
30 #include "system/tpm_backend.h"
31 #include "system/tpm_util.h"
32 #include "trace.h"
33 
34 /* tpm backend property */
35 
36 static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
37                     Error **errp)
38 {
39     TPMBackend **be = object_field_prop_ptr(obj, opaque);
40     char *p;
41 
42     p = g_strdup(*be ? (*be)->id : "");
43     visit_type_str(v, name, &p, errp);
44     g_free(p);
45 }
46 
47 static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
48                     Error **errp)
49 {
50     const Property *prop = opaque;
51     TPMBackend *s, **be = object_field_prop_ptr(obj, prop);
52     char *str;
53 
54     if (!visit_type_str(v, name, &str, errp)) {
55         return;
56     }
57 
58     s = qemu_find_tpm_be(str);
59     if (s == NULL) {
60         error_setg(errp, "Property '%s.%s' can't find value '%s'",
61                    object_get_typename(obj), name, str);
62     } else if (tpm_backend_init(s, TPM_IF(obj), errp) == 0) {
63         *be = s; /* weak reference, avoid cyclic ref */
64     }
65     g_free(str);
66 }
67 
68 static void release_tpm(Object *obj, const char *name, void *opaque)
69 {
70     const Property *prop = opaque;
71     TPMBackend **be = object_field_prop_ptr(obj, prop);
72 
73     if (*be) {
74         tpm_backend_reset(*be);
75     }
76 }
77 
78 const PropertyInfo qdev_prop_tpm = {
79     .type  = "str",
80     .description = "ID of a tpm to use as a backend",
81     .get   = get_tpm,
82     .set   = set_tpm,
83     .release = release_tpm,
84 };
85 
86 /*
87  * Write an error message in the given output buffer.
88  */
89 void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len)
90 {
91     if (out_len >= sizeof(struct tpm_resp_hdr)) {
92         tpm_cmd_set_tag(out, TPM_TAG_RSP_COMMAND);
93         tpm_cmd_set_size(out, sizeof(struct tpm_resp_hdr));
94         tpm_cmd_set_error(out, TPM_FAIL);
95     }
96 }
97 
98 bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len)
99 {
100     if (in_len >= sizeof(struct tpm_req_hdr)) {
101         return tpm_cmd_get_ordinal(in) == TPM_ORD_ContinueSelfTest;
102     }
103 
104     return false;
105 }
106 
107 /*
108  * Send request to a TPM device. We expect a response within one second.
109  */
110 static int tpm_util_request(int fd,
111                             const void *request,
112                             size_t requestlen,
113                             void *response,
114                             size_t responselen)
115 {
116     GPollFD fds[1] = { {.fd = fd, .events = G_IO_IN } };
117     int n;
118 
119     n = write(fd, request, requestlen);
120     if (n < 0) {
121         return -errno;
122     }
123     if (n != requestlen) {
124         return -EFAULT;
125     }
126 
127     /* wait for a second */
128     n = RETRY_ON_EINTR(g_poll(fds, 1, 1000));
129     if (n != 1) {
130         return -errno;
131     }
132 
133     n = read(fd, response, responselen);
134     if (n < sizeof(struct tpm_resp_hdr)) {
135         return -EFAULT;
136     }
137 
138     /* check the header */
139     if (tpm_cmd_get_size(response) != n) {
140         return -EMSGSIZE;
141     }
142 
143     return 0;
144 }
145 
146 /*
147  * A basic test of a TPM device. We expect a well formatted response header
148  * (error response is fine).
149  */
150 static int tpm_util_test(int fd,
151                          const void *request,
152                          size_t requestlen,
153                          uint16_t *return_tag)
154 {
155     char buf[1024];
156     ssize_t ret;
157 
158     ret = tpm_util_request(fd, request, requestlen,
159                            buf, sizeof(buf));
160     if (ret < 0) {
161         return ret;
162     }
163 
164     *return_tag = tpm_cmd_get_tag(buf);
165 
166     return 0;
167 }
168 
169 /*
170  * Probe for the TPM device in the back
171  * Returns 0 on success with the version of the probed TPM set, 1 on failure.
172  */
173 int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
174 {
175     /*
176      * Sending a TPM1.2 command to a TPM2 should return a TPM1.2
177      * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e)
178      *
179      * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the
180      * header.
181      * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag
182      * in the header and an error code.
183      */
184     const struct tpm_req_hdr test_req = {
185         .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
186         .len = cpu_to_be32(sizeof(test_req)),
187         .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
188     };
189 
190     const struct tpm_req_hdr test_req_tpm2 = {
191         .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
192         .len = cpu_to_be32(sizeof(test_req_tpm2)),
193         .ordinal = cpu_to_be32(TPM2_CC_ReadClock),
194     };
195     uint16_t return_tag;
196     int ret;
197 
198     /* Send TPM 2 command */
199     ret = tpm_util_test(tpm_fd, &test_req_tpm2,
200                         sizeof(test_req_tpm2), &return_tag);
201     /* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */
202     if (!ret && return_tag == TPM2_ST_NO_SESSIONS) {
203         *tpm_version = TPM_VERSION_2_0;
204         return 0;
205     }
206 
207     /* Send TPM 1.2 command */
208     ret = tpm_util_test(tpm_fd, &test_req,
209                         sizeof(test_req), &return_tag);
210     if (!ret && return_tag == TPM_TAG_RSP_COMMAND) {
211         *tpm_version = TPM_VERSION_1_2;
212         /* this is a TPM 1.2 */
213         return 0;
214     }
215 
216     *tpm_version = TPM_VERSION_UNSPEC;
217 
218     return 1;
219 }
220 
221 int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
222                              size_t *buffersize)
223 {
224     int ret;
225 
226     switch (tpm_version) {
227     case TPM_VERSION_1_2: {
228         const struct tpm_req_get_buffer_size {
229             struct tpm_req_hdr hdr;
230             uint32_t capability;
231             uint32_t len;
232             uint32_t subcap;
233         } QEMU_PACKED tpm_get_buffer_size = {
234             .hdr = {
235                 .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
236                 .len = cpu_to_be32(sizeof(tpm_get_buffer_size)),
237                 .ordinal = cpu_to_be32(TPM_ORD_GetCapability),
238             },
239             .capability = cpu_to_be32(TPM_CAP_PROPERTY),
240             .len = cpu_to_be32(sizeof(uint32_t)),
241             .subcap = cpu_to_be32(TPM_CAP_PROP_INPUT_BUFFER),
242         };
243         struct tpm_resp_get_buffer_size {
244             struct tpm_resp_hdr hdr;
245             uint32_t len;
246             uint32_t buffersize;
247         } QEMU_PACKED tpm_resp;
248 
249         ret = tpm_util_request(tpm_fd, &tpm_get_buffer_size,
250                                sizeof(tpm_get_buffer_size),
251                                &tpm_resp, sizeof(tpm_resp));
252         if (ret < 0) {
253             return ret;
254         }
255 
256         if (be32_to_cpu(tpm_resp.hdr.len) != sizeof(tpm_resp) ||
257             be32_to_cpu(tpm_resp.len) != sizeof(uint32_t)) {
258             trace_tpm_util_get_buffer_size_hdr_len(
259                 be32_to_cpu(tpm_resp.hdr.len),
260                 sizeof(tpm_resp));
261             trace_tpm_util_get_buffer_size_len(be32_to_cpu(tpm_resp.len),
262                                                sizeof(uint32_t));
263             error_report("tpm_util: Got unexpected response to "
264                          "TPM_GetCapability; errcode: 0x%x",
265                          be32_to_cpu(tpm_resp.hdr.errcode));
266             return -EFAULT;
267         }
268         *buffersize = be32_to_cpu(tpm_resp.buffersize);
269         break;
270     }
271     case TPM_VERSION_2_0: {
272         const struct tpm2_req_get_buffer_size {
273             struct tpm_req_hdr hdr;
274             uint32_t capability;
275             uint32_t property;
276             uint32_t count;
277         } QEMU_PACKED tpm2_get_buffer_size = {
278             .hdr = {
279                 .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
280                 .len = cpu_to_be32(sizeof(tpm2_get_buffer_size)),
281                 .ordinal = cpu_to_be32(TPM2_CC_GetCapability),
282             },
283             .capability = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES),
284             .property = cpu_to_be32(TPM2_PT_MAX_COMMAND_SIZE),
285             .count = cpu_to_be32(2), /* also get TPM2_PT_MAX_RESPONSE_SIZE */
286         };
287         struct tpm2_resp_get_buffer_size {
288             struct tpm_resp_hdr hdr;
289             uint8_t more;
290             uint32_t capability;
291             uint32_t count;
292             uint32_t property1;
293             uint32_t value1;
294             uint32_t property2;
295             uint32_t value2;
296         } QEMU_PACKED tpm2_resp;
297 
298         ret = tpm_util_request(tpm_fd, &tpm2_get_buffer_size,
299                                sizeof(tpm2_get_buffer_size),
300                                &tpm2_resp, sizeof(tpm2_resp));
301         if (ret < 0) {
302             return ret;
303         }
304 
305         if (be32_to_cpu(tpm2_resp.hdr.len) != sizeof(tpm2_resp) ||
306             be32_to_cpu(tpm2_resp.count) != 2) {
307             trace_tpm_util_get_buffer_size_hdr_len2(
308                 be32_to_cpu(tpm2_resp.hdr.len),
309                 sizeof(tpm2_resp));
310             trace_tpm_util_get_buffer_size_len2(
311                 be32_to_cpu(tpm2_resp.count), 2);
312             error_report("tpm_util: Got unexpected response to "
313                          "TPM2_GetCapability; errcode: 0x%x",
314                          be32_to_cpu(tpm2_resp.hdr.errcode));
315             return -EFAULT;
316         }
317         *buffersize = MAX(be32_to_cpu(tpm2_resp.value1),
318                           be32_to_cpu(tpm2_resp.value2));
319         break;
320     }
321     case TPM_VERSION_UNSPEC:
322         return -EFAULT;
323     }
324 
325     trace_tpm_util_get_buffer_size(*buffersize);
326 
327     return 0;
328 }
329 
330 void tpm_sized_buffer_reset(TPMSizedBuffer *tsb)
331 {
332     g_free(tsb->buffer);
333     tsb->buffer = NULL;
334     tsb->size = 0;
335 }
336 
337 void tpm_util_show_buffer(const unsigned char *buffer,
338                           size_t buffer_size, const char *string)
339 {
340     g_autoptr(GString) str = NULL;
341     size_t len, i, l;
342 
343     if (!trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER_CONTENT)) {
344         return;
345     }
346     len = MIN(tpm_cmd_get_size(buffer), buffer_size);
347     trace_tpm_util_show_buffer_header(string, len);
348 
349     for (i = 0; i < len; i += l) {
350         if (str) {
351             g_string_append_c(str, '\n');
352         }
353         l = MIN(len, 16);
354         str = qemu_hexdump_line(str, buffer, l, 1, 0);
355     }
356 
357     g_string_ascii_up(str);
358     trace_tpm_util_show_buffer_content(str->str);
359 }
360