aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/char/tpm/tpm.c59
-rw-r--r--drivers/char/tpm/tpm.h23
-rw-r--r--include/linux/tpm.h4
-rw-r--r--security/keys/trusted.c54
4 files changed, 92 insertions, 48 deletions
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index 677c6e26593f..36e43e50dcef 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -32,12 +32,6 @@
#include "tpm.h"
#include "tpm_eventlog.h"
-enum tpm_const {
- TPM_MINOR = 224, /* officially assigned */
- TPM_BUFSIZE = 4096,
- TPM_NUM_DEVICES = 256,
-};
-
enum tpm_duration {
TPM_SHORT = 0,
TPM_MEDIUM = 1,
@@ -483,6 +477,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
#define TPM_INTERNAL_RESULT_SIZE 200
#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
#define TPM_ORD_GET_CAP cpu_to_be32(101)
+#define TPM_ORD_GET_RANDOM cpu_to_be32(70)
static const struct tpm_input_header tpm_getcap_header = {
.tag = TPM_TAG_RQU_COMMAND,
@@ -1327,6 +1322,58 @@ int tpm_pm_resume(struct device *dev)
}
EXPORT_SYMBOL_GPL(tpm_pm_resume);
+#define TPM_GETRANDOM_RESULT_SIZE 18
+static struct tpm_input_header tpm_getrandom_header = {
+ .tag = TPM_TAG_RQU_COMMAND,
+ .length = cpu_to_be32(14),
+ .ordinal = TPM_ORD_GET_RANDOM
+};
+
+/**
+ * tpm_get_random() - Get random bytes from the tpm's RNG
+ * @chip_num: A specific chip number for the request or TPM_ANY_NUM
+ * @out: destination buffer for the random bytes
+ * @max: the max number of bytes to write to @out
+ *
+ * Returns < 0 on error and the number of bytes read on success
+ */
+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);
+ int err, total = 0, retries = 5;
+ u8 *dest = out;
+
+ chip = tpm_chip_find_get(chip_num);
+ if (chip == NULL)
+ return -ENODEV;
+
+ if (!out || !num_bytes || max > TPM_MAX_RNG_DATA)
+ return -EINVAL;
+
+ do {
+ tpm_cmd.header.in = tpm_getrandom_header;
+ tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
+
+ err = transmit_cmd(chip, &tpm_cmd,
+ TPM_GETRANDOM_RESULT_SIZE + num_bytes,
+ "attempting get random");
+ if (err)
+ break;
+
+ recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len);
+ memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd);
+
+ dest += recd;
+ total += recd;
+ num_bytes -= recd;
+ } while (retries-- && total < max);
+
+ return total ? total : -EIO;
+}
+EXPORT_SYMBOL_GPL(tpm_get_random);
+
/* In case vendor provided release function, call it too.*/
void tpm_dev_vendor_release(struct tpm_chip *chip)
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 917f727e6740..645136eea890 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -28,6 +28,12 @@
#include <linux/io.h>
#include <linux/tpm.h>
+enum tpm_const {
+ TPM_MINOR = 224, /* officially assigned */
+ TPM_BUFSIZE = 4096,
+ TPM_NUM_DEVICES = 256,
+};
+
enum tpm_timeout {
TPM_TIMEOUT = 5, /* msecs */
};
@@ -269,6 +275,21 @@ struct tpm_pcrextend_in {
u8 hash[TPM_DIGEST_SIZE];
}__attribute__((packed));
+/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18
+ * bytes, but 128 is still a relatively large number of random bytes and
+ * anything much bigger causes users of struct tpm_cmd_t to start getting
+ * compiler warnings about stack frame size. */
+#define TPM_MAX_RNG_DATA 128
+
+struct tpm_getrandom_out {
+ __be32 rng_data_len;
+ u8 rng_data[TPM_MAX_RNG_DATA];
+}__attribute__((packed));
+
+struct tpm_getrandom_in {
+ __be32 num_bytes;
+}__attribute__((packed));
+
typedef union {
struct tpm_getcap_params_out getcap_out;
struct tpm_readpubek_params_out readpubek_out;
@@ -277,6 +298,8 @@ typedef union {
struct tpm_pcrread_in pcrread_in;
struct tpm_pcrread_out pcrread_out;
struct tpm_pcrextend_in pcrextend_in;
+ struct tpm_getrandom_in getrandom_in;
+ struct tpm_getrandom_out getrandom_out;
} tpm_cmd_params;
struct tpm_cmd_t {
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index fdc718abf83b..fcb627ff8d3e 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -32,6 +32,7 @@
extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
extern int tpm_send(u32 chip_num, void *cmd, size_t buflen);
+extern int tpm_get_random(u32 chip_num, u8 *data, size_t max);
#else
static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) {
return -ENODEV;
@@ -42,5 +43,8 @@ static inline int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) {
static inline int tpm_send(u32 chip_num, void *cmd, size_t buflen) {
return -ENODEV;
}
+static inline int tpm_get_random(u32 chip_num, u8 *data, size_t max) {
+ return -ENODEV;
+}
#endif
#endif
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 2d5d041f2049..3f163d0489ad 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -369,38 +369,6 @@ static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd,
}
/*
- * get a random value from TPM
- */
-static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len)
-{
- int ret;
-
- INIT_BUF(tb);
- store16(tb, TPM_TAG_RQU_COMMAND);
- store32(tb, TPM_GETRANDOM_SIZE);
- store32(tb, TPM_ORD_GETRANDOM);
- store32(tb, len);
- ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data);
- if (!ret)
- memcpy(buf, tb->data + TPM_GETRANDOM_SIZE, len);
- return ret;
-}
-
-static int my_get_random(unsigned char *buf, int len)
-{
- struct tpm_buf *tb;
- int ret;
-
- tb = kmalloc(sizeof *tb, GFP_KERNEL);
- if (!tb)
- return -ENOMEM;
- ret = tpm_get_random(tb, buf, len);
-
- kfree(tb);
- return ret;
-}
-
-/*
* Lock a trusted key, by extending a selected PCR.
*
* Prevents a trusted key that is sealed to PCRs from being accessed.
@@ -413,8 +381,8 @@ static int pcrlock(const int pcrnum)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- ret = my_get_random(hash, SHA1_DIGEST_SIZE);
- if (ret < 0)
+ ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE);
+ if (ret != SHA1_DIGEST_SIZE)
return ret;
return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0;
}
@@ -429,8 +397,8 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
unsigned char ononce[TPM_NONCE_SIZE];
int ret;
- ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE);
- if (ret < 0)
+ ret = tpm_get_random(TPM_ANY_NUM, ononce, TPM_NONCE_SIZE);
+ if (ret != TPM_NONCE_SIZE)
return ret;
INIT_BUF(tb);
@@ -524,8 +492,8 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
if (ret < 0)
goto out;
- ret = tpm_get_random(tb, td->nonceodd, TPM_NONCE_SIZE);
- if (ret < 0)
+ ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE);
+ if (ret != TPM_NONCE_SIZE)
goto out;
ordinal = htonl(TPM_ORD_SEAL);
datsize = htonl(datalen);
@@ -634,8 +602,8 @@ static int tpm_unseal(struct tpm_buf *tb,
ordinal = htonl(TPM_ORD_UNSEAL);
keyhndl = htonl(SRKHANDLE);
- ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE);
- if (ret < 0) {
+ ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE);
+ if (ret != TPM_NONCE_SIZE) {
pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
return ret;
}
@@ -935,6 +903,7 @@ static int trusted_instantiate(struct key *key, const void *data,
char *datablob;
int ret = 0;
int key_cmd;
+ size_t key_len;
if (datalen <= 0 || datalen > 32767 || !data)
return -EINVAL;
@@ -974,8 +943,9 @@ static int trusted_instantiate(struct key *key, const void *data,
pr_info("trusted_key: key_unseal failed (%d)\n", ret);
break;
case Opt_new:
- ret = my_get_random(payload->key, payload->key_len);
- if (ret < 0) {
+ key_len = payload->key_len;
+ ret = tpm_get_random(TPM_ANY_NUM, payload->key, key_len);
+ if (ret != key_len) {
pr_info("trusted_key: key_create failed (%d)\n", ret);
goto out;
}