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