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