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