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(chip, space->session_tbl[i]);
  42        }
  43}
  44
  45int tpm2_init_space(struct tpm_space *space)
  46{
  47        space->context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
  48        if (!space->context_buf)
  49                return -ENOMEM;
  50
  51        space->session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
  52        if (space->session_buf == NULL) {
  53                kfree(space->context_buf);
  54                return -ENOMEM;
  55        }
  56
  57        return 0;
  58}
  59
  60void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
  61{
  62        mutex_lock(&chip->tpm_mutex);
  63        if (!tpm_chip_start(chip)) {
  64                tpm2_flush_sessions(chip, space);
  65                tpm_chip_stop(chip);
  66        }
  67        mutex_unlock(&chip->tpm_mutex);
  68        kfree(space->context_buf);
  69        kfree(space->session_buf);
  70}
  71
  72static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
  73                             unsigned int *offset, u32 *handle)
  74{
  75        struct tpm_buf tbuf;
  76        struct tpm2_context *ctx;
  77        unsigned int body_size;
  78        int rc;
  79
  80        rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
  81        if (rc)
  82                return rc;
  83
  84        ctx = (struct tpm2_context *)&buf[*offset];
  85        body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
  86        tpm_buf_append(&tbuf, &buf[*offset], body_size);
  87
  88        rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL);
  89        if (rc < 0) {
  90                dev_warn(&chip->dev, "%s: failed with a system error %d\n",
  91                         __func__, rc);
  92                tpm_buf_destroy(&tbuf);
  93                return -EFAULT;
  94        } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
  95                   rc == TPM2_RC_REFERENCE_H0) {
  96                /*
  97                 * TPM_RC_HANDLE means that the session context can't
  98                 * be loaded because of an internal counter mismatch
  99                 * that makes the TPM think there might have been a
 100                 * replay.  This might happen if the context was saved
 101                 * and loaded outside the space.
 102                 *
 103                 * TPM_RC_REFERENCE_H0 means the session has been
 104                 * flushed outside the space
 105                 */
 106                *handle = 0;
 107                tpm_buf_destroy(&tbuf);
 108                return -ENOENT;
 109        } else if (rc > 0) {
 110                dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
 111                         __func__, rc);
 112                tpm_buf_destroy(&tbuf);
 113                return -EFAULT;
 114        }
 115
 116        *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
 117        *offset += body_size;
 118
 119        tpm_buf_destroy(&tbuf);
 120        return 0;
 121}
 122
 123static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
 124                             unsigned int buf_size, unsigned int *offset)
 125{
 126        struct tpm_buf tbuf;
 127        unsigned int body_size;
 128        int rc;
 129
 130        rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
 131        if (rc)
 132                return rc;
 133
 134        tpm_buf_append_u32(&tbuf, handle);
 135
 136        rc = tpm_transmit_cmd(chip, &tbuf, 0, 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
 165void 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(chip, space->context_tbl[i]);
 173
 174        tpm2_flush_sessions(chip, space);
 175}
 176
 177static int tpm2_load_space(struct tpm_chip *chip)
 178{
 179        struct tpm_space *space = &chip->work_space;
 180        unsigned int offset;
 181        int i;
 182        int rc;
 183
 184        for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
 185                if (!space->context_tbl[i])
 186                        continue;
 187
 188                /* sanity check, should never happen */
 189                if (~space->context_tbl[i]) {
 190                        dev_err(&chip->dev, "context table is inconsistent");
 191                        return -EFAULT;
 192                }
 193
 194                rc = tpm2_load_context(chip, space->context_buf, &offset,
 195                                       &space->context_tbl[i]);
 196                if (rc)
 197                        return rc;
 198        }
 199
 200        for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
 201                u32 handle;
 202
 203                if (!space->session_tbl[i])
 204                        continue;
 205
 206                rc = tpm2_load_context(chip, space->session_buf,
 207                                       &offset, &handle);
 208                if (rc == -ENOENT) {
 209                        /* load failed, just forget session */
 210                        space->session_tbl[i] = 0;
 211                } else if (rc) {
 212                        tpm2_flush_space(chip);
 213                        return rc;
 214                }
 215                if (handle != space->session_tbl[i]) {
 216                        dev_warn(&chip->dev, "session restored to wrong handle\n");
 217                        tpm2_flush_space(chip);
 218                        return -EFAULT;
 219                }
 220        }
 221
 222        return 0;
 223}
 224
 225static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)
 226{
 227        u32 vhandle = be32_to_cpup((__be32 *)handle);
 228        u32 phandle;
 229        int i;
 230
 231        i = 0xFFFFFF - (vhandle & 0xFFFFFF);
 232        if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])
 233                return false;
 234
 235        phandle = space->context_tbl[i];
 236        *((__be32 *)handle) = cpu_to_be32(phandle);
 237        return true;
 238}
 239
 240static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
 241{
 242        struct tpm_space *space = &chip->work_space;
 243        unsigned int nr_handles;
 244        u32 attrs;
 245        __be32 *handle;
 246        int i;
 247
 248        i = tpm2_find_cc(chip, cc);
 249        if (i < 0)
 250                return -EINVAL;
 251
 252        attrs = chip->cc_attrs_tbl[i];
 253        nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
 254
 255        handle = (__be32 *)&cmd[TPM_HEADER_SIZE];
 256        for (i = 0; i < nr_handles; i++, handle++) {
 257                if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {
 258                        if (!tpm2_map_to_phandle(space, handle))
 259                                return -EINVAL;
 260                }
 261        }
 262
 263        return 0;
 264}
 265
 266static int tpm_find_and_validate_cc(struct tpm_chip *chip,
 267                                    struct tpm_space *space,
 268                                    const void *cmd, size_t len)
 269{
 270        const struct tpm_header *header = (const void *)cmd;
 271        int i;
 272        u32 cc;
 273        u32 attrs;
 274        unsigned int nr_handles;
 275
 276        if (len < TPM_HEADER_SIZE || !chip->nr_commands)
 277                return -EINVAL;
 278
 279        cc = be32_to_cpu(header->ordinal);
 280
 281        i = tpm2_find_cc(chip, cc);
 282        if (i < 0) {
 283                dev_dbg(&chip->dev, "0x%04X is an invalid command\n",
 284                        cc);
 285                return -EOPNOTSUPP;
 286        }
 287
 288        attrs = chip->cc_attrs_tbl[i];
 289        nr_handles =
 290                4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0));
 291        if (len < TPM_HEADER_SIZE + 4 * nr_handles)
 292                goto err_len;
 293
 294        return cc;
 295err_len:
 296        dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__,
 297                len);
 298        return -EINVAL;
 299}
 300
 301int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd,
 302                       size_t cmdsiz)
 303{
 304        int rc;
 305        int cc;
 306
 307        if (!space)
 308                return 0;
 309
 310        cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz);
 311        if (cc < 0)
 312                return cc;
 313
 314        memcpy(&chip->work_space.context_tbl, &space->context_tbl,
 315               sizeof(space->context_tbl));
 316        memcpy(&chip->work_space.session_tbl, &space->session_tbl,
 317               sizeof(space->session_tbl));
 318        memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE);
 319        memcpy(chip->work_space.session_buf, space->session_buf, PAGE_SIZE);
 320
 321        rc = tpm2_load_space(chip);
 322        if (rc) {
 323                tpm2_flush_space(chip);
 324                return rc;
 325        }
 326
 327        rc = tpm2_map_command(chip, cc, cmd);
 328        if (rc) {
 329                tpm2_flush_space(chip);
 330                return rc;
 331        }
 332
 333        chip->last_cc = cc;
 334        return 0;
 335}
 336
 337static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)
 338{
 339        struct tpm_space *space = &chip->work_space;
 340        int i;
 341
 342        for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)
 343                if (space->session_tbl[i] == 0)
 344                        break;
 345
 346        if (i == ARRAY_SIZE(space->session_tbl))
 347                return false;
 348
 349        space->session_tbl[i] = handle;
 350        return true;
 351}
 352
 353static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)
 354{
 355        int i;
 356
 357        for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
 358                if (alloc) {
 359                        if (!space->context_tbl[i]) {
 360                                space->context_tbl[i] = phandle;
 361                                break;
 362                        }
 363                } else if (space->context_tbl[i] == phandle)
 364                        break;
 365        }
 366
 367        if (i == ARRAY_SIZE(space->context_tbl))
 368                return 0;
 369
 370        return TPM2_HT_TRANSIENT | (0xFFFFFF - i);
 371}
 372
 373static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
 374                                    size_t len)
 375{
 376        struct tpm_space *space = &chip->work_space;
 377        struct tpm_header *header = (struct tpm_header *)rsp;
 378        u32 phandle;
 379        u32 phandle_type;
 380        u32 vhandle;
 381        u32 attrs;
 382        int i;
 383
 384        if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)
 385                return 0;
 386
 387        i = tpm2_find_cc(chip, cc);
 388        /* sanity check, should never happen */
 389        if (i < 0)
 390                return -EFAULT;
 391
 392        attrs = chip->cc_attrs_tbl[i];
 393        if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))
 394                return 0;
 395
 396        phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);
 397        phandle_type = phandle & 0xFF000000;
 398
 399        switch (phandle_type) {
 400        case TPM2_HT_TRANSIENT:
 401                vhandle = tpm2_map_to_vhandle(space, phandle, true);
 402                if (!vhandle)
 403                        goto out_no_slots;
 404
 405                *(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);
 406                break;
 407        case TPM2_HT_HMAC_SESSION:
 408        case TPM2_HT_POLICY_SESSION:
 409                if (!tpm2_add_session(chip, phandle))
 410                        goto out_no_slots;
 411                break;
 412        default:
 413                dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
 414                        __func__, phandle);
 415                break;
 416        }
 417
 418        return 0;
 419out_no_slots:
 420        tpm2_flush_context(chip, phandle);
 421        dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
 422                 phandle);
 423        return -ENOMEM;
 424}
 425
 426struct tpm2_cap_handles {
 427        u8 more_data;
 428        __be32 capability;
 429        __be32 count;
 430        __be32 handles[];
 431} __packed;
 432
 433static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
 434                                  size_t len)
 435{
 436        struct tpm_space *space = &chip->work_space;
 437        struct tpm_header *header = (struct tpm_header *)rsp;
 438        struct tpm2_cap_handles *data;
 439        u32 phandle;
 440        u32 phandle_type;
 441        u32 vhandle;
 442        int i;
 443        int j;
 444
 445        if (cc != TPM2_CC_GET_CAPABILITY ||
 446            be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {
 447                return 0;
 448        }
 449
 450        if (len < TPM_HEADER_SIZE + 9)
 451                return -EFAULT;
 452
 453        data = (void *)&rsp[TPM_HEADER_SIZE];
 454        if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)
 455                return 0;
 456
 457        if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))
 458                return -EFAULT;
 459
 460        for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {
 461                phandle = be32_to_cpup((__be32 *)&data->handles[i]);
 462                phandle_type = phandle & 0xFF000000;
 463
 464                switch (phandle_type) {
 465                case TPM2_HT_TRANSIENT:
 466                        vhandle = tpm2_map_to_vhandle(space, phandle, false);
 467                        if (!vhandle)
 468                                break;
 469
 470                        data->handles[j] = cpu_to_be32(vhandle);
 471                        j++;
 472                        break;
 473
 474                default:
 475                        data->handles[j] = cpu_to_be32(phandle);
 476                        j++;
 477                        break;
 478                }
 479
 480        }
 481
 482        header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);
 483        data->count = cpu_to_be32(j);
 484        return 0;
 485}
 486
 487static int tpm2_save_space(struct tpm_chip *chip)
 488{
 489        struct tpm_space *space = &chip->work_space;
 490        unsigned int offset;
 491        int i;
 492        int rc;
 493
 494        for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
 495                if (!(space->context_tbl[i] && ~space->context_tbl[i]))
 496                        continue;
 497
 498                rc = tpm2_save_context(chip, space->context_tbl[i],
 499                                       space->context_buf, PAGE_SIZE,
 500                                       &offset);
 501                if (rc == -ENOENT) {
 502                        space->context_tbl[i] = 0;
 503                        continue;
 504                } else if (rc)
 505                        return rc;
 506
 507                tpm2_flush_context(chip, space->context_tbl[i]);
 508                space->context_tbl[i] = ~0;
 509        }
 510
 511        for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
 512                if (!space->session_tbl[i])
 513                        continue;
 514
 515                rc = tpm2_save_context(chip, space->session_tbl[i],
 516                                       space->session_buf, PAGE_SIZE,
 517                                       &offset);
 518
 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, PAGE_SIZE);
 565        memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE);
 566
 567        return 0;
 568out:
 569        dev_err(&chip->dev, "%s: error %d\n", __func__, rc);
 570        return rc;
 571}
 572