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, unsigned int buf_size)
  42{
  43        space->context_buf = kzalloc(buf_size, GFP_KERNEL);
  44        if (!space->context_buf)
  45                return -ENOMEM;
  46
  47        space->session_buf = kzalloc(buf_size, GFP_KERNEL);
  48        if (space->session_buf == NULL) {
  49                kfree(space->context_buf);
  50                /* Prevent caller getting a dangling pointer. */
  51                space->context_buf = NULL;
  52                return -ENOMEM;
  53        }
  54
  55        space->buf_size = buf_size;
  56        return 0;
  57}
  58
  59void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
  60{
  61        mutex_lock(&chip->tpm_mutex);
  62        if (!tpm_chip_start(chip)) {
  63                tpm2_flush_sessions(chip, space);
  64                tpm_chip_stop(chip);
  65        }
  66        mutex_unlock(&chip->tpm_mutex);
  67        kfree(space->context_buf);
  68        kfree(space->session_buf);
  69}
  70
  71static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
  72                             unsigned int *offset, u32 *handle)
  73{
  74        struct tpm_buf tbuf;
  75        struct tpm2_context *ctx;
  76        unsigned int body_size;
  77        int rc;
  78
  79        rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
  80        if (rc)
  81                return rc;
  82
  83        ctx = (struct tpm2_context *)&buf[*offset];
  84        body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
  85        tpm_buf_append(&tbuf, &buf[*offset], body_size);
  86
  87        rc = tpm_transmit_cmd(chip, &tbuf, 4, 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, &tbuf, 0, NULL);
 136        if (rc < 0) {
 137                dev_warn(&chip->dev, "%s: failed with a system error %d\n",
 138                         __func__, rc);
 139                tpm_buf_destroy(&tbuf);
 140                return -EFAULT;
 141        } else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
 142                tpm_buf_destroy(&tbuf);
 143                return -ENOENT;
 144        } else if (rc) {
 145                dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
 146                         __func__, rc);
 147                tpm_buf_destroy(&tbuf);
 148                return -EFAULT;
 149        }
 150
 151        body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
 152        if ((*offset + body_size) > buf_size) {
 153                dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
 154                tpm_buf_destroy(&tbuf);
 155                return -ENOMEM;
 156        }
 157
 158        memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
 159        *offset += body_size;
 160        tpm_buf_destroy(&tbuf);
 161        return 0;
 162}
 163
 164void tpm2_flush_space(struct tpm_chip *chip)
 165{
 166        struct tpm_space *space = &chip->work_space;
 167        int i;
 168
 169        for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
 170                if (space->context_tbl[i] && ~space->context_tbl[i])
 171                        tpm2_flush_context(chip, space->context_tbl[i]);
 172
 173        tpm2_flush_sessions(chip, space);
 174}
 175
 176static int tpm2_load_space(struct tpm_chip *chip)
 177{
 178        struct tpm_space *space = &chip->work_space;
 179        unsigned int offset;
 180        int i;
 181        int rc;
 182
 183        for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
 184                if (!space->context_tbl[i])
 185                        continue;
 186
 187                /* sanity check, should never happen */
 188                if (~space->context_tbl[i]) {
 189                        dev_err(&chip->dev, "context table is inconsistent");
 190                        return -EFAULT;
 191                }
 192
 193                rc = tpm2_load_context(chip, space->context_buf, &offset,
 194                                       &space->context_tbl[i]);
 195                if (rc)
 196                        return rc;
 197        }
 198
 199        for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
 200                u32 handle;
 201
 202                if (!space->session_tbl[i])
 203                        continue;
 204
 205                rc = tpm2_load_context(chip, space->session_buf,
 206                                       &offset, &handle);
 207                if (rc == -ENOENT) {
 208                        /* load failed, just forget session */
 209                        space->session_tbl[i] = 0;
 210                } else if (rc) {
 211                        tpm2_flush_space(chip);
 212                        return rc;
 213                }
 214                if (handle != space->session_tbl[i]) {
 215                        dev_warn(&chip->dev, "session restored to wrong handle\n");
 216                        tpm2_flush_space(chip);
 217                        return -EFAULT;
 218                }
 219        }
 220
 221        return 0;
 222}
 223
 224static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)
 225{
 226        u32 vhandle = be32_to_cpup((__be32 *)handle);
 227        u32 phandle;
 228        int i;
 229
 230        i = 0xFFFFFF - (vhandle & 0xFFFFFF);
 231        if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])
 232                return false;
 233
 234        phandle = space->context_tbl[i];
 235        *((__be32 *)handle) = cpu_to_be32(phandle);
 236        return true;
 237}
 238
 239static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
 240{
 241        struct tpm_space *space = &chip->work_space;
 242        unsigned int nr_handles;
 243        u32 attrs;
 244        __be32 *handle;
 245        int i;
 246
 247        i = tpm2_find_cc(chip, cc);
 248        if (i < 0)
 249                return -EINVAL;
 250
 251        attrs = chip->cc_attrs_tbl[i];
 252        nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
 253
 254        handle = (__be32 *)&cmd[TPM_HEADER_SIZE];
 255        for (i = 0; i < nr_handles; i++, handle++) {
 256                if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {
 257                        if (!tpm2_map_to_phandle(space, handle))
 258                                return -EINVAL;
 259                }
 260        }
 261
 262        return 0;
 263}
 264
 265static int tpm_find_and_validate_cc(struct tpm_chip *chip,
 266                                    struct tpm_space *space,
 267                                    const void *cmd, size_t len)
 268{
 269        const struct tpm_header *header = (const void *)cmd;
 270        int i;
 271        u32 cc;
 272        u32 attrs;
 273        unsigned int nr_handles;
 274
 275        if (len < TPM_HEADER_SIZE || !chip->nr_commands)
 276                return -EINVAL;
 277
 278        cc = be32_to_cpu(header->ordinal);
 279
 280        i = tpm2_find_cc(chip, cc);
 281        if (i < 0) {
 282                dev_dbg(&chip->dev, "0x%04X is an invalid command\n",
 283                        cc);
 284                return -EOPNOTSUPP;
 285        }
 286
 287        attrs = chip->cc_attrs_tbl[i];
 288        nr_handles =
 289                4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0));
 290        if (len < TPM_HEADER_SIZE + 4 * nr_handles)
 291                goto err_len;
 292
 293        return cc;
 294err_len:
 295        dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__,
 296                len);
 297        return -EINVAL;
 298}
 299
 300int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd,
 301                       size_t cmdsiz)
 302{
 303        int rc;
 304        int cc;
 305
 306        if (!space)
 307                return 0;
 308
 309        cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz);
 310        if (cc < 0)
 311                return cc;
 312
 313        memcpy(&chip->work_space.context_tbl, &space->context_tbl,
 314               sizeof(space->context_tbl));
 315        memcpy(&chip->work_space.session_tbl, &space->session_tbl,
 316               sizeof(space->session_tbl));
 317        memcpy(chip->work_space.context_buf, space->context_buf,
 318               space->buf_size);
 319        memcpy(chip->work_space.session_buf, space->session_buf,
 320               space->buf_size);
 321
 322        rc = tpm2_load_space(chip);
 323        if (rc) {
 324                tpm2_flush_space(chip);
 325                return rc;
 326        }
 327
 328        rc = tpm2_map_command(chip, cc, cmd);
 329        if (rc) {
 330                tpm2_flush_space(chip);
 331                return rc;
 332        }
 333
 334        chip->last_cc = cc;
 335        return 0;
 336}
 337
 338static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)
 339{
 340        struct tpm_space *space = &chip->work_space;
 341        int i;
 342
 343        for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)
 344                if (space->session_tbl[i] == 0)
 345                        break;
 346
 347        if (i == ARRAY_SIZE(space->session_tbl))
 348                return false;
 349
 350        space->session_tbl[i] = handle;
 351        return true;
 352}
 353
 354static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)
 355{
 356        int i;
 357
 358        for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
 359                if (alloc) {
 360                        if (!space->context_tbl[i]) {
 361                                space->context_tbl[i] = phandle;
 362                                break;
 363                        }
 364                } else if (space->context_tbl[i] == phandle)
 365                        break;
 366        }
 367
 368        if (i == ARRAY_SIZE(space->context_tbl))
 369                return 0;
 370
 371        return TPM2_HT_TRANSIENT | (0xFFFFFF - i);
 372}
 373
 374static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
 375                                    size_t len)
 376{
 377        struct tpm_space *space = &chip->work_space;
 378        struct tpm_header *header = (struct tpm_header *)rsp;
 379        u32 phandle;
 380        u32 phandle_type;
 381        u32 vhandle;
 382        u32 attrs;
 383        int i;
 384
 385        if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)
 386                return 0;
 387
 388        i = tpm2_find_cc(chip, cc);
 389        /* sanity check, should never happen */
 390        if (i < 0)
 391                return -EFAULT;
 392
 393        attrs = chip->cc_attrs_tbl[i];
 394        if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))
 395                return 0;
 396
 397        phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);
 398        phandle_type = phandle & 0xFF000000;
 399
 400        switch (phandle_type) {
 401        case TPM2_HT_TRANSIENT:
 402                vhandle = tpm2_map_to_vhandle(space, phandle, true);
 403                if (!vhandle)
 404                        goto out_no_slots;
 405
 406                *(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);
 407                break;
 408        case TPM2_HT_HMAC_SESSION:
 409        case TPM2_HT_POLICY_SESSION:
 410                if (!tpm2_add_session(chip, phandle))
 411                        goto out_no_slots;
 412                break;
 413        default:
 414                dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
 415                        __func__, phandle);
 416                break;
 417        }
 418
 419        return 0;
 420out_no_slots:
 421        tpm2_flush_context(chip, phandle);
 422        dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
 423                 phandle);
 424        return -ENOMEM;
 425}
 426
 427struct tpm2_cap_handles {
 428        u8 more_data;
 429        __be32 capability;
 430        __be32 count;
 431        __be32 handles[];
 432} __packed;
 433
 434static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
 435                                  size_t len)
 436{
 437        struct tpm_space *space = &chip->work_space;
 438        struct tpm_header *header = (struct tpm_header *)rsp;
 439        struct tpm2_cap_handles *data;
 440        u32 phandle;
 441        u32 phandle_type;
 442        u32 vhandle;
 443        int i;
 444        int j;
 445
 446        if (cc != TPM2_CC_GET_CAPABILITY ||
 447            be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {
 448                return 0;
 449        }
 450
 451        if (len < TPM_HEADER_SIZE + 9)
 452                return -EFAULT;
 453
 454        data = (void *)&rsp[TPM_HEADER_SIZE];
 455        if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)
 456                return 0;
 457
 458        if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))
 459                return -EFAULT;
 460
 461        for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {
 462                phandle = be32_to_cpup((__be32 *)&data->handles[i]);
 463                phandle_type = phandle & 0xFF000000;
 464
 465                switch (phandle_type) {
 466                case TPM2_HT_TRANSIENT:
 467                        vhandle = tpm2_map_to_vhandle(space, phandle, false);
 468                        if (!vhandle)
 469                                break;
 470
 471                        data->handles[j] = cpu_to_be32(vhandle);
 472                        j++;
 473                        break;
 474
 475                default:
 476                        data->handles[j] = cpu_to_be32(phandle);
 477                        j++;
 478                        break;
 479                }
 480
 481        }
 482
 483        header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);
 484        data->count = cpu_to_be32(j);
 485        return 0;
 486}
 487
 488static int tpm2_save_space(struct tpm_chip *chip)
 489{
 490        struct tpm_space *space = &chip->work_space;
 491        unsigned int offset;
 492        int i;
 493        int rc;
 494
 495        for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
 496                if (!(space->context_tbl[i] && ~space->context_tbl[i]))
 497                        continue;
 498
 499                rc = tpm2_save_context(chip, space->context_tbl[i],
 500                                       space->context_buf, space->buf_size,
 501                                       &offset);
 502                if (rc == -ENOENT) {
 503                        space->context_tbl[i] = 0;
 504                        continue;
 505                } else if (rc)
 506                        return rc;
 507
 508                tpm2_flush_context(chip, space->context_tbl[i]);
 509                space->context_tbl[i] = ~0;
 510        }
 511
 512        for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
 513                if (!space->session_tbl[i])
 514                        continue;
 515
 516                rc = tpm2_save_context(chip, space->session_tbl[i],
 517                                       space->session_buf, space->buf_size,
 518                                       &offset);
 519                if (rc == -ENOENT) {
 520                        /* handle error saving session, just forget it */
 521                        space->session_tbl[i] = 0;
 522                } else if (rc < 0) {
 523                        tpm2_flush_space(chip);
 524                        return rc;
 525                }
 526        }
 527
 528        return 0;
 529}
 530
 531int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
 532                      void *buf, size_t *bufsiz)
 533{
 534        struct tpm_header *header = buf;
 535        int rc;
 536
 537        if (!space)
 538                return 0;
 539
 540        rc = tpm2_map_response_header(chip, chip->last_cc, buf, *bufsiz);
 541        if (rc) {
 542                tpm2_flush_space(chip);
 543                goto out;
 544        }
 545
 546        rc = tpm2_map_response_body(chip, chip->last_cc, buf, *bufsiz);
 547        if (rc) {
 548                tpm2_flush_space(chip);
 549                goto out;
 550        }
 551
 552        rc = tpm2_save_space(chip);
 553        if (rc) {
 554                tpm2_flush_space(chip);
 555                goto out;
 556        }
 557
 558        *bufsiz = be32_to_cpu(header->length);
 559
 560        memcpy(&space->context_tbl, &chip->work_space.context_tbl,
 561               sizeof(space->context_tbl));
 562        memcpy(&space->session_tbl, &chip->work_space.session_tbl,
 563               sizeof(space->session_tbl));
 564        memcpy(space->context_buf, chip->work_space.context_buf,
 565               space->buf_size);
 566        memcpy(space->session_buf, chip->work_space.session_buf,
 567               space->buf_size);
 568
 569        return 0;
 570out:
 571        dev_err(&chip->dev, "%s: error %d\n", __func__, rc);
 572        return rc;
 573}
 574