]> git.proxmox.com Git - mirror_qemu.git/blob - hw/tpm/tpm_util.c
tpm: Implement tpm_sized_buffer_reset
[mirror_qemu.git] / hw / tpm / tpm_util.c
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 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 "qapi/error.h"
25 #include "qapi/visitor.h"
26 #include "tpm_util.h"
27 #include "tpm_int.h"
28 #include "exec/memory.h"
29 #include "sysemu/tpm_backend.h"
30 #include "hw/qdev.h"
31
32 #define DEBUG_TPM 0
33
34 #define DPRINTF(fmt, ...) do { \
35 if (DEBUG_TPM) { \
36 fprintf(stderr, "tpm-util:"fmt"\n", ## __VA_ARGS__); \
37 } \
38 } while (0)
39
40 /* tpm backend property */
41
42 static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
43 Error **errp)
44 {
45 DeviceState *dev = DEVICE(obj);
46 TPMBackend **be = qdev_get_prop_ptr(dev, opaque);
47 char *p;
48
49 p = g_strdup(*be ? (*be)->id : "");
50 visit_type_str(v, name, &p, errp);
51 g_free(p);
52 }
53
54 static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
55 Error **errp)
56 {
57 DeviceState *dev = DEVICE(obj);
58 Error *local_err = NULL;
59 Property *prop = opaque;
60 TPMBackend *s, **be = qdev_get_prop_ptr(dev, prop);
61 char *str;
62
63 if (dev->realized) {
64 qdev_prop_set_after_realize(dev, name, errp);
65 return;
66 }
67
68 visit_type_str(v, name, &str, &local_err);
69 if (local_err) {
70 error_propagate(errp, local_err);
71 return;
72 }
73
74 s = qemu_find_tpm_be(str);
75 if (s == NULL) {
76 error_setg(errp, "Property '%s.%s' can't find value '%s'",
77 object_get_typename(obj), prop->name, str);
78 } else if (tpm_backend_init(s, TPM_IF(obj), errp) == 0) {
79 *be = s; /* weak reference, avoid cyclic ref */
80 }
81 g_free(str);
82 }
83
84 static void release_tpm(Object *obj, const char *name, void *opaque)
85 {
86 DeviceState *dev = DEVICE(obj);
87 Property *prop = opaque;
88 TPMBackend **be = qdev_get_prop_ptr(dev, prop);
89
90 if (*be) {
91 tpm_backend_reset(*be);
92 }
93 }
94
95 const PropertyInfo qdev_prop_tpm = {
96 .name = "str",
97 .description = "ID of a tpm to use as a backend",
98 .get = get_tpm,
99 .set = set_tpm,
100 .release = release_tpm,
101 };
102
103 /*
104 * Write an error message in the given output buffer.
105 */
106 void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len)
107 {
108 if (out_len >= sizeof(struct tpm_resp_hdr)) {
109 struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
110
111 resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
112 resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
113 resp->errcode = cpu_to_be32(TPM_FAIL);
114 }
115 }
116
117 bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len)
118 {
119 struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;
120
121 if (in_len >= sizeof(*hdr)) {
122 return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
123 }
124
125 return false;
126 }
127
128 /*
129 * Send request to a TPM device. We expect a response within one second.
130 */
131 static int tpm_util_request(int fd,
132 unsigned char *request,
133 size_t requestlen,
134 unsigned char *response,
135 size_t responselen)
136 {
137 struct tpm_resp_hdr *resp;
138 fd_set readfds;
139 int n;
140 struct timeval tv = {
141 .tv_sec = 1,
142 .tv_usec = 0,
143 };
144
145 n = write(fd, request, requestlen);
146 if (n < 0) {
147 return -errno;
148 }
149 if (n != requestlen) {
150 return -EFAULT;
151 }
152
153 FD_ZERO(&readfds);
154 FD_SET(fd, &readfds);
155
156 /* wait for a second */
157 n = select(fd + 1, &readfds, NULL, NULL, &tv);
158 if (n != 1) {
159 return -errno;
160 }
161
162 n = read(fd, response, responselen);
163 if (n < sizeof(struct tpm_resp_hdr)) {
164 return -EFAULT;
165 }
166
167 resp = (struct tpm_resp_hdr *)response;
168 /* check the header */
169 if (be32_to_cpu(resp->len) != n) {
170 return -EMSGSIZE;
171 }
172
173 return 0;
174 }
175
176 /*
177 * A basic test of a TPM device. We expect a well formatted response header
178 * (error response is fine).
179 */
180 static int tpm_util_test(int fd,
181 unsigned char *request,
182 size_t requestlen,
183 uint16_t *return_tag)
184 {
185 struct tpm_resp_hdr *resp;
186 unsigned char buf[1024];
187 ssize_t ret;
188
189 ret = tpm_util_request(fd, request, requestlen,
190 buf, sizeof(buf));
191 if (ret < 0) {
192 return ret;
193 }
194
195 resp = (struct tpm_resp_hdr *)buf;
196 *return_tag = be16_to_cpu(resp->tag);
197
198 return 0;
199 }
200
201 /*
202 * Probe for the TPM device in the back
203 * Returns 0 on success with the version of the probed TPM set, 1 on failure.
204 */
205 int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
206 {
207 /*
208 * Sending a TPM1.2 command to a TPM2 should return a TPM1.2
209 * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e)
210 *
211 * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the
212 * header.
213 * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag
214 * in the header and an error code.
215 */
216 const struct tpm_req_hdr test_req = {
217 .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
218 .len = cpu_to_be32(sizeof(test_req)),
219 .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
220 };
221
222 const struct tpm_req_hdr test_req_tpm2 = {
223 .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
224 .len = cpu_to_be32(sizeof(test_req_tpm2)),
225 .ordinal = cpu_to_be32(TPM2_CC_ReadClock),
226 };
227 uint16_t return_tag;
228 int ret;
229
230 /* Send TPM 2 command */
231 ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2,
232 sizeof(test_req_tpm2), &return_tag);
233 /* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */
234 if (!ret && return_tag == TPM2_ST_NO_SESSIONS) {
235 *tpm_version = TPM_VERSION_2_0;
236 return 0;
237 }
238
239 /* Send TPM 1.2 command */
240 ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req,
241 sizeof(test_req), &return_tag);
242 if (!ret && return_tag == TPM_TAG_RSP_COMMAND) {
243 *tpm_version = TPM_VERSION_1_2;
244 /* this is a TPM 1.2 */
245 return 0;
246 }
247
248 *tpm_version = TPM_VERSION_UNSPEC;
249
250 return 1;
251 }
252
253 int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
254 size_t *buffersize)
255 {
256 unsigned char buf[1024];
257 int ret;
258
259 switch (tpm_version) {
260 case TPM_VERSION_1_2: {
261 const struct tpm_req_get_buffer_size {
262 struct tpm_req_hdr hdr;
263 uint32_t capability;
264 uint32_t len;
265 uint32_t subcap;
266 } QEMU_PACKED tpm_get_buffer_size = {
267 .hdr = {
268 .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
269 .len = cpu_to_be32(sizeof(tpm_get_buffer_size)),
270 .ordinal = cpu_to_be32(TPM_ORD_GetCapability),
271 },
272 .capability = cpu_to_be32(TPM_CAP_PROPERTY),
273 .len = cpu_to_be32(sizeof(uint32_t)),
274 .subcap = cpu_to_be32(TPM_CAP_PROP_INPUT_BUFFER),
275 };
276 struct tpm_resp_get_buffer_size {
277 struct tpm_resp_hdr hdr;
278 uint32_t len;
279 uint32_t buffersize;
280 } QEMU_PACKED *tpm_resp = (struct tpm_resp_get_buffer_size *)buf;
281
282 ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm_get_buffer_size,
283 sizeof(tpm_get_buffer_size), buf, sizeof(buf));
284 if (ret < 0) {
285 return ret;
286 }
287
288 if (be32_to_cpu(tpm_resp->hdr.len) != sizeof(*tpm_resp) ||
289 be32_to_cpu(tpm_resp->len) != sizeof(uint32_t)) {
290 DPRINTF("tpm_resp->hdr.len = %u, expected = %zu\n",
291 be32_to_cpu(tpm_resp->hdr.len), sizeof(*tpm_resp));
292 DPRINTF("tpm_resp->len = %u, expected = %zu\n",
293 be32_to_cpu(tpm_resp->len), sizeof(uint32_t));
294 error_report("tpm_util: Got unexpected response to "
295 "TPM_GetCapability; errcode: 0x%x",
296 be32_to_cpu(tpm_resp->hdr.errcode));
297 return -EFAULT;
298 }
299 *buffersize = be32_to_cpu(tpm_resp->buffersize);
300 break;
301 }
302 case TPM_VERSION_2_0: {
303 const struct tpm2_req_get_buffer_size {
304 struct tpm_req_hdr hdr;
305 uint32_t capability;
306 uint32_t property;
307 uint32_t count;
308 } QEMU_PACKED tpm2_get_buffer_size = {
309 .hdr = {
310 .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
311 .len = cpu_to_be32(sizeof(tpm2_get_buffer_size)),
312 .ordinal = cpu_to_be32(TPM2_CC_GetCapability),
313 },
314 .capability = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES),
315 .property = cpu_to_be32(TPM2_PT_MAX_COMMAND_SIZE),
316 .count = cpu_to_be32(2), /* also get TPM2_PT_MAX_RESPONSE_SIZE */
317 };
318 struct tpm2_resp_get_buffer_size {
319 struct tpm_resp_hdr hdr;
320 uint8_t more;
321 uint32_t capability;
322 uint32_t count;
323 uint32_t property1;
324 uint32_t value1;
325 uint32_t property2;
326 uint32_t value2;
327 } QEMU_PACKED *tpm2_resp = (struct tpm2_resp_get_buffer_size *)buf;
328
329 ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm2_get_buffer_size,
330 sizeof(tpm2_get_buffer_size), buf, sizeof(buf));
331 if (ret < 0) {
332 return ret;
333 }
334
335 if (be32_to_cpu(tpm2_resp->hdr.len) != sizeof(*tpm2_resp) ||
336 be32_to_cpu(tpm2_resp->count) != 2) {
337 DPRINTF("tpm2_resp->hdr.len = %u, expected = %zu\n",
338 be32_to_cpu(tpm2_resp->hdr.len), sizeof(*tpm2_resp));
339 DPRINTF("tpm2_resp->len = %u, expected = %u\n",
340 be32_to_cpu(tpm2_resp->count), 2);
341 error_report("tpm_util: Got unexpected response to "
342 "TPM2_GetCapability; errcode: 0x%x",
343 be32_to_cpu(tpm2_resp->hdr.errcode));
344 return -EFAULT;
345 }
346 *buffersize = MAX(be32_to_cpu(tpm2_resp->value1),
347 be32_to_cpu(tpm2_resp->value2));
348 break;
349 }
350 case TPM_VERSION_UNSPEC:
351 return -EFAULT;
352 }
353
354 DPRINTF("buffersize of device: %zu\n", *buffersize);
355
356 return 0;
357 }
358
359 void tpm_sized_buffer_reset(TPMSizedBuffer *tsb)
360 {
361 g_free(tsb->buffer);
362 tsb->buffer = NULL;
363 tsb->size = 0;
364 }