diff options
Diffstat (limited to 'drivers/char/tpm/tpm-interface.c')
-rw-r--r-- | drivers/char/tpm/tpm-interface.c | 175 |
1 files changed, 126 insertions, 49 deletions
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index a2688ac2b48f..bd2128e0b56c 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -47,7 +47,7 @@ static int tpm_suspend_pcr; module_param_named(suspend_pcr, tpm_suspend_pcr, uint, 0644); MODULE_PARM_DESC(suspend_pcr, - "PCR to use for dummy writes to faciltate flush on suspend."); + "PCR to use for dummy writes to facilitate flush on suspend."); /* * Array with one entry per ordinal defining the maximum amount @@ -328,8 +328,17 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); -/* - * Internal kernel interface to transmit TPM commands +/** + * tmp_transmit - Internal kernel interface to transmit TPM commands. + * + * @chip: TPM chip to use + * @buf: TPM command buffer + * @bufsiz: length of the TPM command buffer + * @flags: tpm transmit flags - bitmap + * + * Return: + * 0 when the operation is successful. + * A negative number for system errors (errno). */ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, unsigned int flags) @@ -409,31 +418,55 @@ out: return rc; } -#define TPM_DIGEST_SIZE 20 -#define TPM_RET_CODE_IDX 6 - -ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, - int len, unsigned int flags, const char *desc) +/** + * tmp_transmit_cmd - send a tpm command to the device + * The function extracts tpm out header return code + * + * @chip: TPM chip to use + * @buf: TPM command buffer + * @bufsiz: length of the buffer + * @min_rsp_body_length: minimum expected length of response body + * @flags: tpm transmit flags - bitmap + * @desc: command description used in the error message + * + * Return: + * 0 when the operation is successful. + * A negative number for system errors (errno). + * A positive number for a TPM error. + */ +ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *buf, + size_t bufsiz, size_t min_rsp_body_length, + unsigned int flags, const char *desc) { const struct tpm_output_header *header; int err; + ssize_t len; - len = tpm_transmit(chip, (const u8 *)cmd, len, flags); + len = tpm_transmit(chip, (const u8 *)buf, bufsiz, flags); if (len < 0) return len; else if (len < TPM_HEADER_SIZE) return -EFAULT; - header = cmd; + header = buf; + if (len != be32_to_cpu(header->length)) + return -EFAULT; err = be32_to_cpu(header->return_code); if (err != 0 && desc) dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err, desc); + if (err) + return err; + + if (len < min_rsp_body_length + TPM_HEADER_SIZE) + return -EFAULT; - return err; + return 0; } +#define TPM_DIGEST_SIZE 20 +#define TPM_RET_CODE_IDX 6 #define TPM_INTERNAL_RESULT_SIZE 200 #define TPM_ORD_GET_CAP cpu_to_be32(101) #define TPM_ORD_GET_RANDOM cpu_to_be32(70) @@ -445,7 +478,7 @@ static const struct tpm_input_header tpm_getcap_header = { }; ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, - const char *desc) + const char *desc, size_t min_cap_length) { struct tpm_cmd_t tpm_cmd; int rc; @@ -468,8 +501,8 @@ ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = cpu_to_be32(subcap_id); } - rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0, - desc); + rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + min_cap_length, 0, desc); if (!rc) *cap = tpm_cmd.params.getcap_out.cap; return rc; @@ -493,14 +526,13 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) start_cmd.params.startup_in.startup_type = startup_type; return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, 0, - "attempting to start the TPM"); + 0, "attempting to start the TPM"); } int tpm_get_timeouts(struct tpm_chip *chip) { cap_t cap; - unsigned long new_timeout[4]; - unsigned long old_timeout[4]; + unsigned long timeout_old[4], timeout_chip[4], timeout_eff[4]; ssize_t rc; if (chip->flags & TPM_CHIP_FLAG_HAVE_TIMEOUTS) @@ -523,8 +555,8 @@ int tpm_get_timeouts(struct tpm_chip *chip) return 0; } - rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, - "attempting to determine the timeouts"); + rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, NULL, + sizeof(cap.timeout)); if (rc == TPM_ERR_INVALID_POSTINIT) { /* The TPM is not started, we are the first to talk to it. Execute a startup command. */ @@ -533,16 +565,26 @@ int tpm_get_timeouts(struct tpm_chip *chip) return rc; rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, - "attempting to determine the timeouts"); + "attempting to determine the timeouts", + sizeof(cap.timeout)); } - if (rc) + + if (rc) { + dev_err(&chip->dev, + "A TPM error (%zd) occurred attempting to determine the timeouts\n", + rc); return rc; + } - old_timeout[0] = be32_to_cpu(cap.timeout.a); - old_timeout[1] = be32_to_cpu(cap.timeout.b); - old_timeout[2] = be32_to_cpu(cap.timeout.c); - old_timeout[3] = be32_to_cpu(cap.timeout.d); - memcpy(new_timeout, old_timeout, sizeof(new_timeout)); + timeout_old[0] = jiffies_to_usecs(chip->timeout_a); + timeout_old[1] = jiffies_to_usecs(chip->timeout_b); + timeout_old[2] = jiffies_to_usecs(chip->timeout_c); + timeout_old[3] = jiffies_to_usecs(chip->timeout_d); + timeout_chip[0] = be32_to_cpu(cap.timeout.a); + timeout_chip[1] = be32_to_cpu(cap.timeout.b); + timeout_chip[2] = be32_to_cpu(cap.timeout.c); + timeout_chip[3] = be32_to_cpu(cap.timeout.d); + memcpy(timeout_eff, timeout_chip, sizeof(timeout_eff)); /* * Provide ability for vendor overrides of timeout values in case @@ -550,16 +592,24 @@ int tpm_get_timeouts(struct tpm_chip *chip) */ if (chip->ops->update_timeouts != NULL) chip->timeout_adjusted = - chip->ops->update_timeouts(chip, new_timeout); + chip->ops->update_timeouts(chip, timeout_eff); if (!chip->timeout_adjusted) { - /* Don't overwrite default if value is 0 */ - if (new_timeout[0] != 0 && new_timeout[0] < 1000) { - int i; + /* Restore default if chip reported 0 */ + int i; + for (i = 0; i < ARRAY_SIZE(timeout_eff); i++) { + if (timeout_eff[i]) + continue; + + timeout_eff[i] = timeout_old[i]; + chip->timeout_adjusted = true; + } + + if (timeout_eff[0] != 0 && timeout_eff[0] < 1000) { /* timeouts in msec rather usec */ - for (i = 0; i != ARRAY_SIZE(new_timeout); i++) - new_timeout[i] *= 1000; + for (i = 0; i != ARRAY_SIZE(timeout_eff); i++) + timeout_eff[i] *= 1000; chip->timeout_adjusted = true; } } @@ -568,19 +618,20 @@ int tpm_get_timeouts(struct tpm_chip *chip) if (chip->timeout_adjusted) { dev_info(&chip->dev, HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n", - old_timeout[0], new_timeout[0], - old_timeout[1], new_timeout[1], - old_timeout[2], new_timeout[2], - old_timeout[3], new_timeout[3]); + timeout_chip[0], timeout_eff[0], + timeout_chip[1], timeout_eff[1], + timeout_chip[2], timeout_eff[2], + timeout_chip[3], timeout_eff[3]); } - chip->timeout_a = usecs_to_jiffies(new_timeout[0]); - chip->timeout_b = usecs_to_jiffies(new_timeout[1]); - chip->timeout_c = usecs_to_jiffies(new_timeout[2]); - chip->timeout_d = usecs_to_jiffies(new_timeout[3]); + chip->timeout_a = usecs_to_jiffies(timeout_eff[0]); + chip->timeout_b = usecs_to_jiffies(timeout_eff[1]); + chip->timeout_c = usecs_to_jiffies(timeout_eff[2]); + chip->timeout_d = usecs_to_jiffies(timeout_eff[3]); rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap, - "attempting to determine the durations"); + "attempting to determine the durations", + sizeof(cap.duration)); if (rc) return rc; @@ -631,13 +682,14 @@ static int tpm_continue_selftest(struct tpm_chip *chip) struct tpm_cmd_t cmd; cmd.header.in = continue_selftest_header; - rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0, + rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0, 0, "continue selftest"); return rc; } #define TPM_ORDINAL_PCRREAD cpu_to_be32(21) #define READ_PCR_RESULT_SIZE 30 +#define READ_PCR_RESULT_BODY_SIZE 20 static const struct tpm_input_header pcrread_header = { .tag = TPM_TAG_RQU_COMMAND, .length = cpu_to_be32(14), @@ -651,7 +703,8 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) cmd.header.in = pcrread_header; cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx); - rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, 0, + rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, + READ_PCR_RESULT_BODY_SIZE, 0, "attempting to read a pcr value"); if (rc == 0) @@ -714,6 +767,7 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); #define TPM_ORD_PCR_EXTEND cpu_to_be32(20) #define EXTEND_PCR_RESULT_SIZE 34 +#define EXTEND_PCR_RESULT_BODY_SIZE 20 static const struct tpm_input_header pcrextend_header = { .tag = TPM_TAG_RQU_COMMAND, .length = cpu_to_be32(34), @@ -735,13 +789,25 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) struct tpm_cmd_t cmd; int rc; struct tpm_chip *chip; + struct tpm2_digest digest_list[ARRAY_SIZE(chip->active_banks)]; + u32 count = 0; + int i; chip = tpm_chip_find_get(chip_num); if (chip == NULL) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_TPM2) { - rc = tpm2_pcr_extend(chip, pcr_idx, hash); + memset(digest_list, 0, sizeof(digest_list)); + + for (i = 0; i < ARRAY_SIZE(chip->active_banks) && + chip->active_banks[i] != TPM2_ALG_ERROR; i++) { + digest_list[i].alg_id = chip->active_banks[i]; + memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE); + count++; + } + + rc = tpm2_pcr_extend(chip, pcr_idx, count, digest_list); tpm_put_ops(chip); return rc; } @@ -749,7 +815,8 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) cmd.header.in = pcrextend_header; cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0, + rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, + EXTEND_PCR_RESULT_BODY_SIZE, 0, "attempting extend a PCR value"); tpm_put_ops(chip); @@ -853,7 +920,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen) if (chip == NULL) return -ENODEV; - rc = tpm_transmit_cmd(chip, cmd, buflen, 0, "attempting tpm_cmd"); + rc = tpm_transmit_cmd(chip, cmd, buflen, 0, 0, "attempting tpm_cmd"); tpm_put_ops(chip); return rc; @@ -955,7 +1022,8 @@ int tpm_pm_suspend(struct device *dev) cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr); memcpy(cmd.params.pcrextend_in.hash, dummy_hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0, + rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, + EXTEND_PCR_RESULT_BODY_SIZE, 0, "extending dummy pcr before suspend"); } @@ -963,7 +1031,7 @@ int tpm_pm_suspend(struct device *dev) for (try = 0; try < TPM_RETRY; try++) { cmd.header.in = savestate_header; rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 0, - NULL); + 0, NULL); /* * If the TPM indicates that it is too busy to respond to @@ -1025,7 +1093,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) { struct tpm_chip *chip; struct tpm_cmd_t tpm_cmd; - u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA); + u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA), rlength; int err, total = 0, retries = 5; u8 *dest = out; @@ -1048,11 +1116,20 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) err = tpm_transmit_cmd(chip, &tpm_cmd, TPM_GETRANDOM_RESULT_SIZE + num_bytes, + offsetof(struct tpm_getrandom_out, + rng_data), 0, "attempting get random"); if (err) break; recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len); + + rlength = be32_to_cpu(tpm_cmd.header.out.length); + if (rlength < offsetof(struct tpm_getrandom_out, rng_data) + + recd) { + total = -EFAULT; + break; + } memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd); dest += recd; |