linux/drivers/char/tpm/tpm2-space.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2016 Intel Corporation
   3 *
   4 * Authors:
   5 * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
   6 *
   7 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
   8 *
   9 * This file contains TPM2 protocol implementations of the commands
  10 * used by the kernel internally.
  11 *
  12 * This program is free software; you can redistribute it and/or
  13 * modify it under the terms of the GNU General Public License
  14 * as published by the Free Software Foundation; version 2
  15 * of the License.
  16 */
  17
  18#include <linux/gfp.h>
  19#include <asm/unaligned.h>
  20#include "tpm.h"
  21
  22enum tpm2_handle_types {
  23        TPM2_HT_HMAC_SESSION    = 0x02000000,
  24        TPM2_HT_POLICY_SESSION  = 0x03000000,
  25        TPM2_HT_TRANSIENT       = 0x80000000,
  26};
  27
  28struct tpm2_context {
  29        __be64 sequence;
  30        __be32 saved_handle;
  31        __be32 hierarchy;
  32        __be16 blob_size;
  33} __packed;
  34
  35static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)
  36{
  37        int i;
  38
  39        for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
  40                if (space->session_tbl[i])
  41                        tpm2_flush_context_cmd(chip, space->session_tbl[i],
  42                                               TPM_TRANSMIT_NESTED);
  43        }
  44}
  45
  46int tpm2_init_space(struct tpm_space *space)
  47{
  48        space->context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
  49        if (!space->context_buf)
  50                return -ENOMEM;
  51
  52        space->session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
  53        if (space->session_buf == NULL) {
  54                kfree(space->context_buf);
  55                return -ENOMEM;
  56        }
  57
  58        return 0;
  59}
  60
  61void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
  62{
  63        mutex_lock(&chip->tpm_mutex);
  64        tpm2_flush_sessions(chip, space);
  65        mutex_unlock(&chip->tpm_mutex);
  66        kfree(space->context_buf);
  67        kfree(space->session_buf);
  68}
  69
  70static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
  71                             unsigned int *offset, u32 *handle)
  72{
  73        struct tpm_buf tbuf;
  74        struct tpm2_context *ctx;
  75        unsigned int body_size;
  76        int rc;
  77
  78        rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
  79        if (rc)
  80                return rc;
  81
  82        ctx = (struct tpm2_context *)&buf[*offset];
  83        body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
  84        tpm_buf_append(&tbuf, &buf[*offset], body_size);
  85
  86        rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4,
  87                              TPM_TRANSMIT_NESTED, NULL);
  88        if (rc < 0) {
  89                dev_warn(&chip->dev, "%s: failed with a system error %d\n",
  90                         __func__, rc);
  91                tpm_buf_destroy(&tbuf);
  92                return -EFAULT;
  93        } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
  94                   rc == TPM2_RC_REFERENCE_H0) {
  95                /*
  96                 * TPM_RC_HANDLE means that the session context can't
  97                 * be loaded because of an internal counter mismatch
  98                 * that makes the TPM think there might have been a
  99                 * replay.  This might happen if the context was saved
 100                 * and loaded outside the space.
 101                 *
 102                 * TPM_RC_REFERENCE_H0 means the session has been
 103                 * flushed outside the space
 104                 */
 105                *handle = 0;
 106                tpm_buf_destroy(&tbuf);
 107                return -ENOENT;
 108        } else if (rc > 0) {
 109                dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
 110                         __func__, rc);
 111                tpm_buf_destroy(&tbuf);
 112                return -EFAULT;
 113        }
 114
 115        *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
 116        *offset += body_size;
 117
 118        tpm_buf_destroy(&tbuf);
 119        return 0;
 120}
 121
 122static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
 123                             unsigned int buf_size, unsigned int *offset)
 124{
 125        struct tpm_buf tbuf;
 126        unsigned int body_size;
 127        int rc;
 128
 129        rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
 130        if (rc)
 131                return rc;
 132
 133        tpm_buf_append_u32(&tbuf, handle);
 134
 135        rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0,
 136                              TPM_TRANSMIT_NESTED, NULL);
 137        if (rc < 0) {
 138                dev_warn(&chip->dev, "%s: failed with a system error %d\n",
 139                         __func__, rc);
 140                tpm_buf_destroy(&tbuf);
 141                return -EFAULT;
 142        } else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
 143                tpm_buf_destroy(&tbuf);
 144                return -ENOENT;
 145        } else if (rc) {
 146                dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
 147                         __func__, rc);
 148                tpm_buf_destroy(&tbuf);
 149                return -EFAULT;
 150        }
 151
 152        body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
 153        if ((*offset + body_size) > buf_size) {
 154                dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
 155                tpm_buf_destroy(&tbuf);
 156                return -ENOMEM;
 157        }
 158
 159        memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
 160        *offset += body_size;
 161        tpm_buf_destroy(&tbuf);
 162        return 0;
 163}
 164
 165static void tpm2_flush_space(struct tpm_chip *chip)
 166{
 167        struct tpm_space *space = &chip->work_space;
 168        int i;
 169
 170        for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
 171                if (space->context_tbl[i] && ~space->context_tbl[i])
 172                        tpm2_flush_context_cmd(chip, space->context_tbl[i],
 173                                               TPM_TRANSMIT_NESTED);
 174
 175        tpm2_flush_sessions(chip, space);
 176}
 177
 178static int tpm2_load_space(struct tpm_chip *chip)
 179{
 180        struct tpm_space *space = &chip->work_space;
 181        unsigned int offset;
 182        int i;
 183        int rc;
 184
 185        for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
 186                if (!space->context_tbl[i])
 187                        continue;
 188
 189                /* sanity check, should never happen */
 190                if (~space->context_tbl[i]) {
 191                        dev_err(&chip->dev, "context table is inconsistent");
 192                        return -EFAULT;
 193                }
 194
 195                rc = tpm2_load_context(chip, space->context_buf, &offset,
 196                                       &space->context_tbl[i]);
 197                if (rc)
 198                        return rc;
 199        }
 200
 201        for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
 202                u32 handle;
 203
 204                if (!space->session_tbl[i])
 205                        continue;
 206
 207                rc = tpm2_load_context(chip, space->session_buf,
 208                                       &offset, &handle);
 209                if (rc == -ENOENT) {
 210                        /* load failed, just forget session */
 211                        space->session_tbl[i] = 0;
 212                } else if (rc) {
 213                        tpm2_flush_space(chip);
 214                        return rc;
 215                }
 216                if (handle != space->session_tbl[i]) {
 217                        dev_warn(&chip->dev, "session restored to wrong handle\n");
 218                        tpm2_flush_space(chip);
 219                        return -EFAULT;
 220                }
 221        }
 222
 223        return 0;
 224}
 225
 226static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)
 227{
 228        u32 vhandle = be32_to_cpup((__be32 *)handle);
 229        u32 phandle;
 230        int i;
 231
 232        i = 0xFFFFFF - (vhandle & 0xFFFFFF);
 233        if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])
 234                return false;
 235
 236        phandle = space->context_tbl[i];
 237        *((__be32 *)handle) = cpu_to_be32(phandle);
 238        return true;
 239}
 240
 241static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
 242{
 243        struct tpm_space *space = &chip->work_space;
 244        unsigned int nr_handles;
 245        u32 attrs;
 246        __be32 *handle;
 247        int i;
 248
 249        i = tpm2_find_cc(chip, cc);
 250        if (i < 0)
 251                return -EINVAL;
 252
 253        attrs = chip->cc_attrs_tbl[i];
 254        nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
 255
 256        handle = (__be32 *)&cmd[TPM_HEADER_SIZE];
 257        for (i = 0; i < nr_handles; i++, handle++) {
 258                if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {
 259                        if (!tpm2_map_to_phandle(space, handle))
 260                                return -EINVAL;
 261                }
 262        }
 263
 264        return 0;
 265}
 266
 267int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
 268                       u8 *cmd)
 269{
 270        int rc;
 271
 272        if (!space)
 273                return 0;
 274
 275        memcpy(&chip->work_space.context_tbl, &space->context_tbl,
 276               sizeof(space->context_tbl));
 277        memcpy(&chip->work_space.session_tbl, &space->session_tbl,
 278               sizeof(space->session_tbl));
 279        memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE);
 280        memcpy(chip->work_space.session_buf, space->session_buf, PAGE_SIZE);
 281
 282        rc = tpm2_load_space(chip);
 283        if (rc) {
 284                tpm2_flush_space(chip);
 285                return rc;
 286        }
 287
 288        rc = tpm2_map_command(chip, cc, cmd);
 289        if (rc) {
 290                tpm2_flush_space(chip);
 291                return rc;
 292        }
 293
 294        return 0;
 295}
 296
 297static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)
 298{
 299        struct tpm_space *space = &chip->work_space;
 300        int i;
 301
 302        for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)
 303                if (space->session_tbl[i] == 0)
 304                        break;
 305
 306        if (i == ARRAY_SIZE(space->session_tbl))
 307                return false;
 308
 309        space->session_tbl[i] = handle;
 310        return true;
 311}
 312
 313static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)
 314{
 315        int i;
 316
 317        for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
 318                if (alloc) {
 319                        if (!space->context_tbl[i]) {
 320                                space->context_tbl[i] = phandle;
 321                                break;
 322                        }
 323                } else if (space->context_tbl[i] == phandle)
 324                        break;
 325        }
 326
 327        if (i == ARRAY_SIZE(space->context_tbl))
 328                return 0;
 329
 330        return TPM2_HT_TRANSIENT | (0xFFFFFF - i);
 331}
 332
 333static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
 334                                    size_t len)
 335{
 336        struct tpm_space *space = &chip->work_space;
 337        struct tpm_output_header *header = (void *)rsp;
 338        u32 phandle;
 339        u32 phandle_type;
 340        u32 vhandle;
 341        u32 attrs;
 342        int i;
 343
 344        if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)
 345                return 0;
 346
 347        i = tpm2_find_cc(chip, cc);
 348        /* sanity check, should never happen */
 349        if (i < 0)
 350                return -EFAULT;
 351
 352        attrs = chip->cc_attrs_tbl[i];
 353        if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))
 354                return 0;
 355
 356        phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);
 357        phandle_type = phandle & 0xFF000000;
 358
 359        switch (phandle_type) {
 360        case TPM2_HT_TRANSIENT:
 361                vhandle = tpm2_map_to_vhandle(space, phandle, true);
 362                if (!vhandle)
 363                        goto out_no_slots;
 364
 365                *(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);
 366                break;
 367        case TPM2_HT_HMAC_SESSION:
 368        case TPM2_HT_POLICY_SESSION:
 369                if (!tpm2_add_session(chip, phandle))
 370                        goto out_no_slots;
 371                break;
 372        default:
 373                dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
 374                        __func__, phandle);
 375                break;
 376        };
 377
 378        return 0;
 379out_no_slots:
 380        tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_NESTED);
 381        dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
 382                 phandle);
 383        return -ENOMEM;
 384}
 385
 386struct tpm2_cap_handles {
 387        u8 more_data;
 388        __be32 capability;
 389        __be32 count;
 390        __be32 handles[];
 391} __packed;
 392
 393static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
 394                                  size_t len)
 395{
 396        struct tpm_space *space = &chip->work_space;
 397        struct tpm_output_header *header = (void *)rsp;
 398        struct tpm2_cap_handles *data;
 399        u32 phandle;
 400        u32 phandle_type;
 401        u32 vhandle;
 402        int i;
 403        int j;
 404
 405        if (cc != TPM2_CC_GET_CAPABILITY ||
 406            be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {
 407                return 0;
 408        }
 409
 410        if (len < TPM_HEADER_SIZE + 9)
 411                return -EFAULT;
 412
 413        data = (void *)&rsp[TPM_HEADER_SIZE];
 414        if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)
 415                return 0;
 416
 417        if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))
 418                return -EFAULT;
 419
 420        for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {
 421                phandle = be32_to_cpup((__be32 *)&data->handles[i]);
 422                phandle_type = phandle & 0xFF000000;
 423
 424                switch (phandle_type) {
 425                case TPM2_HT_TRANSIENT:
 426                        vhandle = tpm2_map_to_vhandle(space, phandle, false);
 427                        if (!vhandle)
 428                                break;
 429
 430                        data->handles[j] = cpu_to_be32(vhandle);
 431                        j++;
 432                        break;
 433
 434                default:
 435                        data->handles[j] = cpu_to_be32(phandle);
 436                        j++;
 437                        break;
 438                }
 439
 440        }
 441
 442        header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);
 443        data->count = cpu_to_be32(j);
 444        return 0;
 445}
 446
 447static int tpm2_save_space(struct tpm_chip *chip)
 448{
 449        struct tpm_space *space = &chip->work_space;
 450        unsigned int offset;
 451        int i;
 452        int rc;
 453
 454        for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
 455                if (!(space->context_tbl[i] && ~space->context_tbl[i]))
 456                        continue;
 457
 458                rc = tpm2_save_context(chip, space->context_tbl[i],
 459                                       space->context_buf, PAGE_SIZE,
 460                                       &offset);
 461                if (rc == -ENOENT) {
 462                        space->context_tbl[i] = 0;
 463                        continue;
 464                } else if (rc)
 465                        return rc;
 466
 467                tpm2_flush_context_cmd(chip, space->context_tbl[i],
 468                                       TPM_TRANSMIT_NESTED);
 469                space->context_tbl[i] = ~0;
 470        }
 471
 472        for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
 473                if (!space->session_tbl[i])
 474                        continue;
 475
 476                rc = tpm2_save_context(chip, space->session_tbl[i],
 477                                       space->session_buf, PAGE_SIZE,
 478                                       &offset);
 479
 480                if (rc == -ENOENT) {
 481                        /* handle error saving session, just forget it */
 482                        space->session_tbl[i] = 0;
 483                } else if (rc < 0) {
 484                        tpm2_flush_space(chip);
 485                        return rc;
 486                }
 487        }
 488
 489        return 0;
 490}
 491
 492int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
 493                      u32 cc, u8 *buf, size_t *bufsiz)
 494{
 495        struct tpm_output_header *header = (void *)buf;
 496        int rc;
 497
 498        if (!space)
 499                return 0;
 500
 501        rc = tpm2_map_response_header(chip, cc, buf, *bufsiz);
 502        if (rc) {
 503                tpm2_flush_space(chip);
 504                return rc;
 505        }
 506
 507        rc = tpm2_map_response_body(chip, cc, buf, *bufsiz);
 508        if (rc) {
 509                tpm2_flush_space(chip);
 510                return rc;
 511        }
 512
 513        rc = tpm2_save_space(chip);
 514        if (rc) {
 515                tpm2_flush_space(chip);
 516                return rc;
 517        }
 518
 519        *bufsiz = be32_to_cpu(header->length);
 520
 521        memcpy(&space->context_tbl, &chip->work_space.context_tbl,
 522               sizeof(space->context_tbl));
 523        memcpy(&space->session_tbl, &chip->work_space.session_tbl,
 524               sizeof(space->session_tbl));
 525        memcpy(space->context_buf, chip->work_space.context_buf, PAGE_SIZE);
 526        memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE);
 527
 528        return 0;
 529}
 530