From: David H. <dho...@re...> - 2018-08-21 15:57:00
|
Hi Denis, Jarkko, James, I managed to recover the TPM library patchset that I was working on about four years ago in the process of trying to write an asymmetric key subtype that could use the TPM. Here it is in it's original glory for reference. I ended up leaving it because Jarkko's tpm-2 stuff broke it and I had other things to work on. If it's deemed worthwhile, I can try to forward port it to current Linus/mastet. Note that one or two of the preliminary patches may be upstream. The patches can also be found here: http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=tpm-lib Thanks, David --- David Howells (23): TPM: Add new TPMs to the tail of the list to prevent inadvertent change of dev TPM: Provide a facility for a userspace TPM emulator TPM: Provide a platform driver for the user emulator driver TPM: Expose struct tpm_chip and related find_get and put functions TPM: Use struct tpm_chip rather than chip number as interface parameter TPM: Move ordinal values from interface file to header with other ordinals TPM: Consolidate tpm_send(), transmit_cmd() and tpm_transmit() TPMLIB: Break TPM bits out of security/keys/trusted.c TPMLIB: Do some source cleanups TPMLIB: Better format calls to TSS_*hmac*() TPMLIB: Put banner comments on public TPM library functions TPMLIB: Create tpm_{even,odd}_nonce structs to represent nonces TPMLIB: Rename store8() and storebytes() TPMLIB: Make store_s() take a void* data argument, not unsigned char* TPMLIB: Use __be32 rather than int32_t and use cpu_to_beX() and co. TPMLIB: Put more comments into the HMAC generation functions TPMLIB: Provide a wrapper to load bytes out of the reply TPMLIB: Encapsulate XOR-based encryption with authkey derivative TPMLIB: Add some debugging code TPMLIB: Implement call to TPM_CreateWrapKey TPMLIB: Implement call to TPM_LoadKey2 TPMLIB: Provide call for TPM_FlushSpecific TPM: Add an asymmetric key subtype for handling TPM-based keys crypto/asymmetric_keys/Kconfig | 7 crypto/asymmetric_keys/Makefile | 1 crypto/asymmetric_keys/tpm_key.c | 73 ++ crypto/asymmetric_keys/tpm_key.h | 19 crypto/asymmetric_keys/tpm_key_parser.c | 212 +++++ drivers/char/tpm/Kconfig | 13 drivers/char/tpm/Makefile | 3 drivers/char/tpm/tpm-dev.c | 17 drivers/char/tpm/tpm-interface.c | 304 ++++---- drivers/char/tpm/tpm-library.c | 1216 +++++++++++++++++++++++++++++++ drivers/char/tpm/tpm-library.h | 119 +++ drivers/char/tpm/tpm-sysfs.c | 33 - drivers/char/tpm/tpm.h | 15 drivers/char/tpm/tpm_infineon.c | 6 drivers/char/tpm/tpm_user_emul.c | 694 ++++++++++++++++++ include/linux/tpm.h | 123 +++ include/linux/tpm_command.h | 22 - include/linux/wait.h | 11 security/integrity/ima/ima.h | 2 security/integrity/ima/ima_crypto.c | 4 security/integrity/ima/ima_init.c | 19 security/integrity/ima/ima_queue.c | 4 security/keys/trusted.c | 690 +----------------- security/keys/trusted.h | 80 -- 24 files changed, 2712 insertions(+), 975 deletions(-) create mode 100644 crypto/asymmetric_keys/tpm_key.c create mode 100644 crypto/asymmetric_keys/tpm_key.h create mode 100644 crypto/asymmetric_keys/tpm_key_parser.c create mode 100644 drivers/char/tpm/tpm-library.c create mode 100644 drivers/char/tpm/tpm-library.h create mode 100644 drivers/char/tpm/tpm_user_emul.c |
From: David H. <dho...@re...> - 2018-08-21 15:57:08
|
Add newly registered TPMs to the tail of the list, not the beginning, so that things that are specifying TPM_ANY_NUM don't find that the device they're using has inadvertently changed. Adding a second device would break IMA, for instance. Signed-off-by: David Howells <dho...@re...> Reviewed-by: Jason Gunthorpe <jgu...@ob...> Signed-off-by: Peter Huewe <pet...@gm...> cc: st...@vg... --- drivers/char/tpm/tpm-interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 6af17002a115..cfb9089887bd 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -1122,7 +1122,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, /* Make chip available */ spin_lock(&driver_lock); - list_add_rcu(&chip->list, &tpm_chip_list); + list_add_tail_rcu(&chip->list, &tpm_chip_list); spin_unlock(&driver_lock); return chip; |
From: Jarkko S. <jar...@li...> - 2018-08-24 06:24:23
|
Use "tpm" instead of "TPM" in the short summary. On Tue, Aug 21, 2018 at 04:56:56PM +0100, David Howells wrote: > Add newly registered TPMs to the tail of the list, not the beginning, so that > things that are specifying TPM_ANY_NUM don't find that the device they're > using has inadvertently changed. Adding a second device would break IMA, for > instance. > > Signed-off-by: David Howells <dho...@re...> > Reviewed-by: Jason Gunthorpe <jgu...@ob...> > Signed-off-by: Peter Huewe <pet...@gm...> > cc: st...@vg... Usually I add Cc-tag before signed-off-by's (and have the first c in upper case). Peter's singed-off-by should be accompanied with a co-developed-by tag if he has participated to the development of this commit. As far as I see signed-off-by without co-developed-by makes sense in two occasions: * You own the subsystem tree i.e. you have to sign the changes that you include part of your pull request. * You are the initial authoer of the change. > --- > > drivers/char/tpm/tpm-interface.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c > index 6af17002a115..cfb9089887bd 100644 > --- a/drivers/char/tpm/tpm-interface.c > +++ b/drivers/char/tpm/tpm-interface.c > @@ -1122,7 +1122,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, > > /* Make chip available */ > spin_lock(&driver_lock); > - list_add_rcu(&chip->list, &tpm_chip_list); I would add here a comment just as a remainder. > + list_add_tail_rcu(&chip->list, &tpm_chip_list); > spin_unlock(&driver_lock); > > return chip; /Jarkko |
From: Jason G. <jg...@zi...> - 2018-08-21 18:59:57
|
On Tue, Aug 21, 2018 at 04:56:56PM +0100, David Howells wrote: > Add newly registered TPMs to the tail of the list, not the beginning, so that > things that are specifying TPM_ANY_NUM don't find that the device they're > using has inadvertently changed. Adding a second device would break IMA, for > instance. > > Signed-off-by: David Howells <dho...@re...> > Reviewed-by: Jason Gunthorpe <jgu...@ob...> > Signed-off-by: Peter Huewe <pet...@gm...> > cc: st...@vg... > --- We really should apply this patch... Jason |
From: David H. <dho...@re...> - 2018-08-21 15:57:12
|
Provide a misc device file (/dev/tpm_emul) by which a userspace TPM emulator can set up a virtual TPM device under the control of the TPM frontend. The way this works is: (1) The emulator opens /dev/tpm_emul which is provided by the tpm_user driver. (2) tpm_user registers a TPM device and the tpm driver creates a /dev/tpmN misc device for the trousers package and suchlike to access. (3) The emulator sits in read() on the emulator device waiting for a command to come through. (4) tpm_user passes requests from /dev/tpmN to the emulator's read() call. (5) The emulator processes the request. (6) The emulator either write()'s the reply or calls ioctl(fd,0,0) to cancel the command. (7) The emulator goes back to read() to wait for the next command. (8) tpm_user passes the reply back to the tpm driver which passes it back to /dev/tpmN. When the emulator closes /dev/tpm_emul, the TPM driver is unregistered and the /dev/tpmN misc device is then removed. Any outstanding requests are aborted and -EIO will be returned from then on. Multiple TPMs can be registered. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/Kconfig | 13 + drivers/char/tpm/Makefile | 1 drivers/char/tpm/tpm_user_emul.c | 672 ++++++++++++++++++++++++++++++++++++++ include/linux/wait.h | 11 + 4 files changed, 697 insertions(+) create mode 100644 drivers/char/tpm/tpm_user_emul.c diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index c54cac3f8bc8..c33ebd8504ec 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -122,4 +122,17 @@ config TCG_XEN To compile this driver as a module, choose M here; the module will be called xen-tpmfront. +config TCG_USER_EMUL + tristate "Userspace TPM emulation interface for development purposes" + depends on DEBUG_KERNEL + ---help--- + Provide a userspace TPM emulation interface through a misc device + interface (/dev/tpm_emul). This can be used to enable development of + TPM-based services by providing a target TPM that can be safely + trashed or for computers that don't have a physical TPM. + + This driver should _NOT_ be included in production kernels as it + might be possible to use it to fool various kernel security features + (such as IMA). + endif # TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 4d85dd681b81..b179052cd81b 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o obj-$(CONFIG_TCG_ST33_I2C) += tpm_i2c_stm_st33.o obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o +obj-$(CONFIG_TCG_USER_EMUL) += tpm_user_emul.o diff --git a/drivers/char/tpm/tpm_user_emul.c b/drivers/char/tpm/tpm_user_emul.c new file mode 100644 index 000000000000..b96350592bca --- /dev/null +++ b/drivers/char/tpm/tpm_user_emul.c @@ -0,0 +1,672 @@ +/* TPM userspace emulation driver + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dho...@re...) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "TPM_USER: "fmt +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/miscdevice.h> +#include <linux/workqueue.h> +#include <linux/completion.h> +#include "tpm.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Howells <dho...@re...>"); + +#define TIS_SHORT_TIMEOUT 750 /* ms */ +#define TIS_LONG_TIMEOUT 2000 /* 2 sec */ + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +/* + * Packet of data going to/from the TPM. We only permit one command + * at a time, so we don't need to deal with chains of packets. + */ +struct tpm_user_packet { + unsigned size; + unsigned cancellation; + u8 buffer[]; +}; + +/* + * Emulator state. + */ +enum tpm_user_status { + TPM_USER_INITIALISING, + TPM_USER_IDLE, + TPM_USER_SENDING, + TPM_USER_AWAITING_REPLY, + TPM_USER_CANCELLING, + TPM_USER_GOT_REPLY, + TPM_USER_CANCELLED, + TPM_USER_EIO, +}; + +struct tpm_user_state { + struct work_struct initialiser_work; + struct completion initialiser_done; + struct file *file; + struct platform_device *pdev; + struct tpm_chip *chip; + wait_queue_head_t wq; + struct tpm_user_packet *to_emulator_q; + struct tpm_user_packet *from_emulator_q; + spinlock_t lock; + enum tpm_user_status status; + int initialiser_error; +}; + +/* + * Read the emulator status. + */ +static u8 tpm_user_status(struct tpm_chip *chip) +{ + struct tpm_user_state *state = chip->vendor.priv; + enum tpm_user_status status = ACCESS_ONCE(state->status); + + if (status == TPM_USER_GOT_REPLY) + return 1; + if (status == TPM_USER_CANCELLED || + status == TPM_USER_EIO) + return 2; + return 0; +} + +/* + * Find out if a request has been cancelled. + */ +static bool tpm_user_is_req_cancelled(struct tpm_chip *chip, u8 status) +{ + struct tpm_user_state *state = chip->vendor.priv; + struct tpm_user_packet *pkt = NULL; + + if (status != 2) + return false; + + spin_lock(&state->lock); + if (state->status == TPM_USER_CANCELLED) { + pkt = state->from_emulator_q; + state->from_emulator_q = NULL; + state->status = TPM_USER_IDLE; + } + spin_unlock(&state->lock); + kfree(pkt); + return true; +} + +/* + * Send data to the emulator. + */ +static int tpm_user_send(struct tpm_chip *chip, u8 *buf, size_t len) +{ + struct tpm_user_state *state = chip->vendor.priv; + struct tpm_user_packet *pkt; + int ret; + + kenter(",%*phN,%zu", min_t(int, len, 16), buf, len); + + pkt = kmalloc(sizeof(struct tpm_user_packet) + len, GFP_KERNEL); + if (!pkt) + return -ENOMEM; + pkt->size = len; + memcpy(pkt->buffer, buf, len); + + spin_lock(&state->lock); + switch (state->status) { + case TPM_USER_IDLE: + state->to_emulator_q = pkt; + state->status = TPM_USER_SENDING; + ret = 0; + break; + default: + dev_err(chip->dev, "Sending in state %u\n", state->status); + case TPM_USER_EIO: + kfree(pkt); + ret = -EIO; + break; + } + spin_unlock(&state->lock); + if (ret == 0) + wake_up(&state->wq); + return ret; +} + +/* + * Allow the TPM emulator to read requests from the driver + */ +static ssize_t tpm_user_read(struct file *file, char __user *buffer, + size_t buflen, loff_t *pos) +{ + struct tpm_user_state *state = file->private_data; + struct tpm_user_packet *pkt; + unsigned long copied; + ssize_t ret; + + kenter("{%u},,%zu,", state->status, buflen); + +again: + mutex_lock(&file_inode(file)->i_mutex); + + ret = -EAGAIN; + spin_lock(&state->lock); + if (state->status == TPM_USER_EIO) { + ret = -EIO; + goto out; + } + + if (state->status != TPM_USER_SENDING) { + if (file->f_flags & O_NONBLOCK) + goto out; + pr_devel("sleeping\n"); + wait_event_cmd_interruptible( + state->wq, + state->status == TPM_USER_SENDING || + state->status == TPM_USER_EIO, + spin_unlock(&state->lock), + spin_lock(&state->lock)); + pr_devel("woken\n"); + + ret = -ERESTARTSYS; + if (signal_pending(current)) + goto out; + ret = -EIO; + if (state->status == TPM_USER_EIO) + goto out; + } + + pkt = state->to_emulator_q; + pr_devel("dequeued send(%u)\n", pkt->size); + ret = -EMSGSIZE; + if (pkt->size > buflen) + goto out; + + /* Claim responsibility for the packet. */ + state->status = TPM_USER_AWAITING_REPLY; + state->to_emulator_q = NULL; + spin_unlock(&state->lock); + + copied = copy_to_user(buffer, pkt->buffer, pkt->size); + spin_lock(&state->lock); + + if (copied != 0) { + /* Ugh - the emulator went splat. Discard the request and + * reject all further requests. */ + kfree(pkt); + dev_err(state->chip->dev, "Emulator EFAULT in read\n"); + state->status = TPM_USER_EIO; + ret = -EFAULT; + } else if (state->status == TPM_USER_CANCELLING) { + pr_devel("cancel\n"); + state->status = TPM_USER_CANCELLED; + ret = -ECANCELED; + } else { + ret = pkt->size; + } + +out: + spin_unlock(&state->lock); + mutex_unlock(&file_inode(file)->i_mutex); + if (ret == -ECANCELED) + goto again; + kleave(" = %zd", ret); + return ret; +} + +/* + * Allow the TPM emulator to respond to requests + * + * The buffer is should contain a packet with at least TPM_HEADER_SIZE bytes of + * data in it. + */ +static ssize_t tpm_user_write(struct file *file, + const char __user *data, + size_t datalen, + loff_t *pos) +{ + struct tpm_user_state *state = file->private_data; + struct tpm_user_packet *pkt; + unsigned expected; + __be32 tmpbe; + ssize_t ret; + + kenter("{%u},,%zu,", state->status, datalen); + + /* Sanity checking the reply before we get any locks. */ + if (datalen == 0) { + dev_err(state->chip->dev, "Empty reply\n"); + return -EMSGSIZE; + } + + if (datalen < TPM_HEADER_SIZE) { + dev_err(state->chip->dev, "Data packet missing TPM header\n"); + return -EMSGSIZE; + } + + pkt = kmalloc(sizeof(struct tpm_user_packet) + datalen, GFP_KERNEL); + if (!pkt) + return -ENOMEM; + pkt->size = datalen; + ret = -EFAULT; + if (copy_from_user(pkt->buffer, data, datalen) != 0) + goto err_free; + + pr_debug("got reply %*phN\n", min_t(int, datalen, 16), pkt->buffer); + + memcpy(&tmpbe, pkt->buffer + 2, sizeof(tmpbe)); + expected = be32_to_cpu(tmpbe); + if (expected != datalen) { + dev_err(state->chip->dev, "Data packet size (%zu) != expected (%x)\n", + datalen, expected); + ret = -EMSGSIZE; + goto err_free; + } + + mutex_lock(&file_inode(file)->i_mutex); + spin_lock(&state->lock); + + switch (state->status) { + case TPM_USER_AWAITING_REPLY: + BUG_ON(state->from_emulator_q != NULL); + state->from_emulator_q = pkt; + state->status = TPM_USER_GOT_REPLY; + pkt = NULL; + ret = datalen; + break; + + case TPM_USER_CANCELLING: + state->status = TPM_USER_CANCELLED; + ret = -ECANCELED; + break; + + case TPM_USER_EIO: + ret = -EIO; + break; + + default: + dev_err(state->chip->dev, "Reply unexpected in state (%u)\n", + state->status); + ret = -EPROTO; + break; + } + + spin_unlock(&state->lock); + mutex_unlock(&file_inode(file)->i_mutex); + if (ret != -EPROTO) + wake_up(&state->wq); +err_free: + kfree(pkt); + kleave(" = %zd", ret); + return ret; +} + +/* + * Allow the TPM emulator to cancel a request with ioctl(fd,0,0). + */ +static long tpm_user_ioctl(struct file *file, unsigned cmd, unsigned long data) +{ + struct tpm_user_state *state = file->private_data; + long ret; + + kenter("{%u},%x,%lx", state->status, cmd, data); + + if (cmd != 0 && data != 0) + return -ENOIOCTLCMD; + + mutex_lock(&file_inode(file)->i_mutex); + spin_lock(&state->lock); + + switch (state->status) { + case TPM_USER_AWAITING_REPLY: + case TPM_USER_CANCELLING: + state->status = TPM_USER_CANCELLED; + ret = 0; + break; + + case TPM_USER_EIO: + ret = -EIO; + break; + + default: + dev_err(state->chip->dev, + "Cancellation unexpected in state (%u)\n", + state->status); + ret = -EPROTO; + break; + } + + spin_unlock(&state->lock); + mutex_unlock(&file_inode(file)->i_mutex); + wake_up(&state->wq); + kleave(" = %ld", ret); + return ret; +} + +/* + * Receive data from the emulator. + */ +static int tpm_user_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct tpm_user_state *state = chip->vendor.priv; + struct tpm_user_packet *pkt; + int ret; + + kenter("{%u},,%zu", state->status, count); + + spin_lock(&state->lock); + + pkt = state->from_emulator_q; + state->from_emulator_q = NULL; + switch (state->status) { + case TPM_USER_GOT_REPLY: + state->status = TPM_USER_IDLE; + BUG_ON(!pkt); + ret = pkt->size; + break; + + case TPM_USER_CANCELLED: + state->status = TPM_USER_IDLE; + ret = -ECANCELED; + break; + + case TPM_USER_EIO: + ret = -EIO; + break; + + default: + dev_err(chip->dev, "TPM data recv in unexpected state (%u)\n", + state->status); + ret = -EIO; + break; + } + + spin_unlock(&state->lock); + + if (pkt) { + if (ret > 0) { + if (pkt->size > count) { + dev_err(chip->dev, "Received excess data\n"); + ret = -EIO; + } else { + memcpy(buf, pkt->buffer, pkt->size); + } + } + kfree(pkt); + } + + kleave(" = %d", ret); + return ret; +} + +/* + * Abort the current request. + */ +static void tpm_user_cancel(struct tpm_chip *chip) +{ + struct tpm_user_state *state = chip->vendor.priv; + + kenter("{%u}", state->status); + + spin_lock(&state->lock); + + switch (state->status) { + case TPM_USER_SENDING: + case TPM_USER_AWAITING_REPLY: + case TPM_USER_GOT_REPLY: + kfree(state->to_emulator_q); + state->to_emulator_q = NULL; + kfree(state->from_emulator_q); + state->from_emulator_q = NULL; + state->status = TPM_USER_CANCELLING; + break; + default: + break; + } + + if (state->status == TPM_USER_CANCELLING) { + DECLARE_WAITQUEUE(waiter, current); + + for (;;) { + prepare_to_wait(&state->wq, + &waiter, TASK_UNINTERRUPTIBLE); + if (state->status == TPM_USER_CANCELLED || + state->status == TPM_USER_EIO) + break; + spin_unlock(&state->lock); + schedule_timeout(10 * HZ); + spin_lock(&state->lock); + } + finish_wait(&state->wq, &waiter); + + if (state->status != TPM_USER_CANCELLED) + state->status = TPM_USER_EIO; + else + state->status = TPM_USER_IDLE; + } + + spin_unlock(&state->lock); +} + +/* + * Allow the TPM emulator to wait for a request + */ +static unsigned int tpm_user_poll(struct file *file, + struct poll_table_struct *poll) +{ + struct tpm_user_state *state = file->private_data; + enum tpm_user_status status = ACCESS_ONCE(state->status); + unsigned mask; + + poll_wait(file, &state->wq, poll); + mask = 0; + + switch (status) { + case TPM_USER_SENDING: + return POLLIN; + case TPM_USER_AWAITING_REPLY: + return POLLOUT; + case TPM_USER_CANCELLING: + return POLLPRI; + case TPM_USER_EIO: + return POLLERR; + default: + return 0; + } +} + +static const struct tpm_class_ops tpm_user_class = { + .status = tpm_user_status, + .recv = tpm_user_recv, + .send = tpm_user_send, + .cancel = tpm_user_cancel, + .req_complete_mask = 1, + .req_complete_val = 1, + .req_canceled = tpm_user_is_req_cancelled, +}; + +/* + * Asynchronous initialiser. We have to do it this way because we get timeouts + * and run a selftest on the TPM - which means doing reads and writes on the + * file. + */ +static void tpm_user_initialiser(struct work_struct *work) +{ + struct tpm_user_state *state = + container_of(work, struct tpm_user_state, initialiser_work); + struct platform_device *pdev; + struct tpm_chip *chip; + int ret = -ENODEV; + + kenter(""); + + pdev = platform_device_register_simple("tpm_user", -1, NULL, 0); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); + goto err_dev; + } + state->pdev = pdev; + + pr_devel("Registering TPM\n"); + + ret = -ENODEV; + chip = tpm_register_hardware(&pdev->dev, &tpm_user_class); + if (!chip) + goto err_reg; + + chip->vendor.priv = state; + init_waitqueue_head(&chip->vendor.read_queue); + init_waitqueue_head(&chip->vendor.int_queue); + INIT_LIST_HEAD(&chip->vendor.list); + state->chip = chip; + + /* Default timeouts */ + chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + + /* We will need to operate the communication channel */ + spin_lock(&state->lock); + ret = -EIO; + if (state->status == TPM_USER_EIO) + goto err_tpm_locked; + state->status = TPM_USER_IDLE; + spin_unlock(&state->lock); + + /* Not all variants of the emulator support getting the timeout */ + pr_devel("Getting timeouts\n"); + if (tpm_get_timeouts(chip)) + dev_err(&pdev->dev, "Could not get TPM timeouts and durations\n"); + + pr_devel("Performing selftest\n"); + ret = tpm_do_selftest(chip); + if (ret < 0) { + dev_err(&pdev->dev, "TPM self test failed\n"); + goto err_tpm; + } + + ret = 0; +out: + state->initialiser_error = ret; + kleave(" = %d", ret); + complete(&state->initialiser_done); + return; + +err_tpm: + spin_lock(&state->lock); + if (state->status != TPM_USER_EIO) + state->status = TPM_USER_EIO; +err_tpm_locked: + spin_unlock(&state->lock); + wake_up(&state->wq); + tpm_remove_hardware(chip->dev); +err_reg: + platform_device_unregister(pdev); +err_dev: + goto out; +} + +/* + * Allow the TPM emulator to create a virtual TPM. + */ +static int tpm_user_open(struct inode *inode, struct file *file) +{ + struct tpm_user_state *state; + + kenter(""); + + state = kzalloc(sizeof(struct tpm_user_state), GFP_KERNEL); + if (!state) + return -ENOMEM; + state->file = file; + state->status = TPM_USER_INITIALISING; + spin_lock_init(&state->lock); + INIT_WORK(&state->initialiser_work, tpm_user_initialiser); + init_completion(&state->initialiser_done); + init_waitqueue_head(&state->wq); + + file->private_data = state; + + /* The TPM registration must be done in another thread because the the + * process will involve self-testing the TPM and will thus need to + * communicate through this file. + */ + schedule_work(&state->initialiser_work); + kleave(" = 0"); + return 0; +} + +/* + * Release and clean up a virtual TPM. + */ +static int tpm_user_release(struct inode *inode, struct file *file) +{ + struct tpm_user_state *state = file->private_data; + int ret; + + kenter(""); + + pr_devel("forcing EIO state\n"); + spin_lock(&state->lock); + state->status = TPM_USER_EIO; + spin_unlock(&state->lock); + wake_up(&state->wq); + + wait_for_completion(&state->initialiser_done); + + ret = state->initialiser_error; + if (ret == 0) { + pr_devel("removing bits\n"); + tpm_remove_hardware(state->chip->dev); + platform_device_unregister(state->pdev); + } + kfree(state->to_emulator_q); + kfree(state->from_emulator_q); + kfree(state); + kleave(" = %d", ret); + return ret; +} + +static const struct file_operations tpm_user_fops = { + .owner = THIS_MODULE, + .open = tpm_user_open, + .release = tpm_user_release, + .read = tpm_user_read, + .write = tpm_user_write, + .unlocked_ioctl = tpm_user_ioctl, + .poll = tpm_user_poll, + .llseek = noop_llseek, +}; + +static struct miscdevice tpm_user_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tpm_emul", + .fops = &tpm_user_fops, +}; + +/* + * Initialise a device + */ +static __init int tpm_user_mod_init(void) +{ + return misc_register(&tpm_user_dev); +} +device_initcall(tpm_user_mod_init); + +static __exit void tpm_user_mod_exit(void) +{ + misc_deregister(&tpm_user_dev); +} +module_exit(tpm_user_mod_exit); diff --git a/include/linux/wait.h b/include/linux/wait.h index bd68819f0815..8b443229997e 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -294,6 +294,10 @@ do { \ (void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0, \ cmd1; schedule(); cmd2) +#define __wait_event_cmd_interruptible(wq, condition, cmd1, cmd2) \ + (void)___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, \ + cmd1; schedule(); cmd2) + /** * wait_event_cmd - sleep until a condition gets true * @wq: the waitqueue to wait on @@ -315,6 +319,13 @@ do { \ __wait_event_cmd(wq, condition, cmd1, cmd2); \ } while (0) +#define wait_event_cmd_interruptible(wq, condition, cmd1, cmd2) \ +do { \ + if (condition) \ + break; \ + __wait_event_cmd_interruptible(wq, condition, cmd1, cmd2); \ +} while (0) + #define __wait_event_interruptible(wq, condition) \ ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, \ schedule()) |
From: Jason G. <jg...@zi...> - 2018-08-21 18:57:33
|
On Tue, Aug 21, 2018 at 04:57:03PM +0100, David Howells wrote: > Provide a misc device file (/dev/tpm_emul) by which a userspace TPM emulator > can set up a virtual TPM device under the control of the TPM frontend. The > way this works is: > > (1) The emulator opens /dev/tpm_emul which is provided by the tpm_user > driver. > > (2) tpm_user registers a TPM device and the tpm driver creates a /dev/tpmN > misc device for the trousers package and suchlike to access. > > (3) The emulator sits in read() on the emulator device waiting for a command > to come through. > > (4) tpm_user passes requests from /dev/tpmN to the emulator's read() call. > > (5) The emulator processes the request. > > (6) The emulator either write()'s the reply or calls ioctl(fd,0,0) to cancel > the command. > > (7) The emulator goes back to read() to wait for the next command. > > (8) tpm_user passes the reply back to the tpm driver which passes it back to > /dev/tpmN. > > When the emulator closes /dev/tpm_emul, the TPM driver is unregistered and the > /dev/tpmN misc device is then removed. Any outstanding requests are aborted > and -EIO will be returned from then on. Multiple TPMs can be registered. > > Signed-off-by: David Howells <dho...@re...> > --- > > drivers/char/tpm/Kconfig | 13 + > drivers/char/tpm/Makefile | 1 > drivers/char/tpm/tpm_user_emul.c | 672 ++++++++++++++++++++++++++++++++++++++ > include/linux/wait.h | 11 + > 4 files changed, 697 insertions(+) > create mode 100644 drivers/char/tpm/tpm_user_emul.c This looks to duplicate the vtpm stuff... Jason |
From: Jarkko S. <jar...@li...> - 2018-08-24 06:29:26
|
On Tue, Aug 21, 2018 at 12:31:40PM -0600, Jason Gunthorpe wrote: > On Tue, Aug 21, 2018 at 04:57:03PM +0100, David Howells wrote: > > Provide a misc device file (/dev/tpm_emul) by which a userspace TPM emulator > > can set up a virtual TPM device under the control of the TPM frontend. The > > way this works is: > > > > (1) The emulator opens /dev/tpm_emul which is provided by the tpm_user > > driver. > > > > (2) tpm_user registers a TPM device and the tpm driver creates a /dev/tpmN > > misc device for the trousers package and suchlike to access. > > > > (3) The emulator sits in read() on the emulator device waiting for a command > > to come through. > > > > (4) tpm_user passes requests from /dev/tpmN to the emulator's read() call. > > > > (5) The emulator processes the request. > > > > (6) The emulator either write()'s the reply or calls ioctl(fd,0,0) to cancel > > the command. > > > > (7) The emulator goes back to read() to wait for the next command. > > > > (8) tpm_user passes the reply back to the tpm driver which passes it back to > > /dev/tpmN. > > > > When the emulator closes /dev/tpm_emul, the TPM driver is unregistered and the > > /dev/tpmN misc device is then removed. Any outstanding requests are aborted > > and -EIO will be returned from then on. Multiple TPMs can be registered. > > > > Signed-off-by: David Howells <dho...@re...> > > --- > > > > drivers/char/tpm/Kconfig | 13 + > > drivers/char/tpm/Makefile | 1 > > drivers/char/tpm/tpm_user_emul.c | 672 ++++++++++++++++++++++++++++++++++++++ > > include/linux/wait.h | 11 + > > 4 files changed, 697 insertions(+) > > create mode 100644 drivers/char/tpm/tpm_user_emul.c > > This looks to duplicate the vtpm stuff... Yeah, this is a duplicate to tpm_vtpm_proxy. > > Jason /Jarkko |
From: David H. <dho...@re...> - 2018-08-21 15:57:18
|
Provide a platform driver for the user emulator driver. This seems to be necessary to stop tpm_chip_find_get() from blowing up because it assumes unconditionally that any device will have a driver attached: if (try_module_get(pos->dev->driver->owner)) { However, this doesn't then work right because if I remove the TPM device and re-add it, the tpm ID isn't recycled (ie, /dev/tpm0 becomes unavailable and the new TPM is /dev/tpm1). Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm_user_emul.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_user_emul.c b/drivers/char/tpm/tpm_user_emul.c index b96350592bca..e5b13358c71e 100644 --- a/drivers/char/tpm/tpm_user_emul.c +++ b/drivers/char/tpm/tpm_user_emul.c @@ -656,17 +656,39 @@ static struct miscdevice tpm_user_dev = { .fops = &tpm_user_fops, }; +static struct platform_driver tpm_user_drv = { + .driver = { + .name = "tpm_user", + .owner = THIS_MODULE, + /* .pm = &tpm_user_pm, -- do we need pm since there's no h/w? */ + }, +}; + /* * Initialise a device */ static __init int tpm_user_mod_init(void) { - return misc_register(&tpm_user_dev); + int ret; + + ret = platform_driver_register(&tpm_user_drv); + if (ret < 0) + return ret; + + ret = misc_register(&tpm_user_dev); + if (ret < 0) + goto error_dev; + return 0; + +error_dev: + platform_driver_unregister(&tpm_user_drv); + return ret; } device_initcall(tpm_user_mod_init); static __exit void tpm_user_mod_exit(void) { misc_deregister(&tpm_user_dev); + platform_driver_unregister(&tpm_user_drv); } module_exit(tpm_user_mod_exit); |
From: Jarkko S. <jar...@li...> - 2018-08-24 06:30:28
|
On Tue, Aug 21, 2018 at 04:57:09PM +0100, David Howells wrote: > Provide a platform driver for the user emulator driver. This seems to be > necessary to stop tpm_chip_find_get() from blowing up because it assumes > unconditionally that any device will have a driver attached: > > if (try_module_get(pos->dev->driver->owner)) { > > However, this doesn't then work right because if I remove the TPM device and > re-add it, the tpm ID isn't recycled (ie, /dev/tpm0 becomes unavailable and > the new TPM is /dev/tpm1). > > Signed-off-by: David Howells <dho...@re...> Again, a duplicate. /Jarkko |
From: David H. <dho...@re...> - 2018-08-21 15:57:25
|
Expose struct tpm_chip and related find_get and put functions so that TPM-using code can make sure it uses the same TPM for any related set of operations. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-interface.c | 19 ++++++++++++++++--- drivers/char/tpm/tpm.h | 5 ----- include/linux/tpm.h | 10 ++++++++++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index cfb9089887bd..b8f1df5b64fe 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -636,10 +636,11 @@ static int tpm_continue_selftest(struct tpm_chip *chip) return rc; } -/* - * tpm_chip_find_get - return tpm_chip for given chip number +/** + * tpm_chip_find_get - Look up a TPM chip by device index + * @chip_num: The index number of the chip to use or TPM_ANY_NUM */ -static struct tpm_chip *tpm_chip_find_get(int chip_num) +struct tpm_chip *tpm_chip_find_get(int chip_num) { struct tpm_chip *pos, *chip = NULL; @@ -656,6 +657,18 @@ static struct tpm_chip *tpm_chip_find_get(int chip_num) rcu_read_unlock(); return chip; } +EXPORT_SYMBOL_GPL(tpm_chip_find_get); + +/** + * tpm_chip_put - Release a previously looked up TPM chip + * @chip: The chip to release + */ +void tpm_chip_put(struct tpm_chip *chip) +{ + if (chip) + module_put(chip->dev->driver->owner); +} +EXPORT_SYMBOL_GPL(tpm_chip_put); #define TPM_ORDINAL_PCRREAD cpu_to_be32(21) #define READ_PCR_RESULT_SIZE 30 diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index e4d0888d2eab..df6ffceb3429 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -115,11 +115,6 @@ struct tpm_chip { #define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor) -static inline void tpm_chip_put(struct tpm_chip *chip) -{ - module_put(chip->dev->driver->owner); -} - static inline int tpm_read_index(int base, int index) { outb(index, base); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 8350c538b486..44c8cad7132d 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -46,11 +46,21 @@ struct tpm_class_ops { #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE) +extern struct tpm_chip *tpm_chip_find_get(int chip_num); +extern void tpm_chip_put(struct tpm_chip *chip); + 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 struct tpm_chip *tpm_chip_find_get(int chip_num) +{ + return NULL; +} +static inline void tpm_chip_put(struct tpm_chip *chip) +{ +} static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) { return -ENODEV; } |
From: Jason G. <jg...@zi...> - 2018-08-21 19:03:28
|
On Tue, Aug 21, 2018 at 04:57:16PM +0100, David Howells wrote: > Expose struct tpm_chip and related find_get and put functions so that > TPM-using code can make sure it uses the same TPM for any related set of > operations. > > Signed-off-by: David Howells <dho...@re...> > --- > drivers/char/tpm/tpm-interface.c | 19 ++++++++++++++++--- > drivers/char/tpm/tpm.h | 5 ----- > include/linux/tpm.h | 10 ++++++++++ > 3 files changed, 26 insertions(+), 8 deletions(-) I think this is already in the kernel .. Stephen did it. Jason |
From: David H. <dho...@re...> - 2018-08-21 18:35:51
|
Jason Gunthorpe <jg...@zi...> wrote: > I think this is already in the kernel .. Stephen did it. As I said in the cover: one or two of these patches may already be upstream. It's the state of the branch I had four years ago. David |
From: David H. <dho...@re...> - 2018-08-21 15:57:32
|
Use struct tpm_chip rather than chip number as interface parameter for most interface functions. This allows the client to be sure about the consistency of what device it uses. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-interface.c | 76 ++++++++------------------------- drivers/char/tpm/tpm-sysfs.c | 2 - include/linux/tpm.h | 16 ++++--- security/integrity/ima/ima.h | 2 - security/integrity/ima/ima_crypto.c | 4 +- security/integrity/ima/ima_init.c | 19 +++++--- security/integrity/ima/ima_queue.c | 4 +- security/keys/trusted.c | 80 ++++++++++++++++++++++------------- 8 files changed, 96 insertions(+), 107 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index b8f1df5b64fe..29c2ce5cfc69 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -678,25 +678,9 @@ static struct tpm_input_header pcrread_header = { .ordinal = TPM_ORDINAL_PCRREAD }; -int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) -{ - int rc; - struct tpm_cmd_t cmd; - - cmd.header.in = pcrread_header; - cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx); - rc = transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, - "attempting to read a pcr value"); - - if (rc == 0) - memcpy(res_buf, cmd.params.pcrread_out.pcr_result, - TPM_DIGEST_SIZE); - return rc; -} - /** * tpm_pcr_read - read a pcr value - * @chip_num: tpm idx # or ANY + * @chip: The chip to pass the request to * @pcr_idx: pcr idx to retrieve * @res_buf: TPM_PCR value * size of res_buf is 20 bytes (or NULL if you don't care) @@ -705,23 +689,26 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) * isn't, protect against the chip disappearing, by incrementing * the module usage count. */ -int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) +int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) { - struct tpm_chip *chip; int rc; + struct tpm_cmd_t cmd; - chip = tpm_chip_find_get(chip_num); - if (chip == NULL) - return -ENODEV; - rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf); - tpm_chip_put(chip); + cmd.header.in = pcrread_header; + cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx); + rc = transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, + "attempting to read a pcr value"); + + if (rc == 0) + memcpy(res_buf, cmd.params.pcrread_out.pcr_result, + TPM_DIGEST_SIZE); return rc; } EXPORT_SYMBOL_GPL(tpm_pcr_read); /** * tpm_pcr_extend - extend pcr value with hash - * @chip_num: tpm idx # or AN& + * @chip: The chip to pass the request to * @pcr_idx: pcr idx to extend * @hash: hash value used to extend pcr value * @@ -737,24 +724,15 @@ static struct tpm_input_header pcrextend_header = { .ordinal = TPM_ORD_PCR_EXTEND }; -int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) +int tpm_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash) { struct tpm_cmd_t cmd; - int rc; - struct tpm_chip *chip; - - chip = tpm_chip_find_get(chip_num); - if (chip == NULL) - return -ENODEV; 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 = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, - "attempting extend a PCR value"); - - tpm_chip_put(chip); - return rc; + return transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, + "attempting extend a PCR value"); } EXPORT_SYMBOL_GPL(tpm_pcr_extend); @@ -821,19 +799,9 @@ int tpm_do_selftest(struct tpm_chip *chip) } EXPORT_SYMBOL_GPL(tpm_do_selftest); -int tpm_send(u32 chip_num, void *cmd, size_t buflen) +int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen) { - struct tpm_chip *chip; - int rc; - - chip = tpm_chip_find_get(chip_num); - if (chip == NULL) - return -ENODEV; - - rc = transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd"); - - tpm_chip_put(chip); - return rc; + return transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd"); } EXPORT_SYMBOL_GPL(tpm_send); @@ -1010,15 +978,14 @@ static struct tpm_input_header tpm_getrandom_header = { /** * tpm_get_random() - Get random bytes from the tpm's RNG - * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @chip: The chip to pass the request to * @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) +int tpm_get_random(struct tpm_chip *chip, 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; @@ -1027,10 +994,6 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) if (!out || !num_bytes || max > TPM_MAX_RNG_DATA) return -EINVAL; - chip = tpm_chip_find_get(chip_num); - if (chip == NULL) - return -ENODEV; - do { tpm_cmd.header.in = tpm_getrandom_header; tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes); @@ -1049,7 +1012,6 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) num_bytes -= recd; } while (retries-- && total < max); - tpm_chip_put(chip); return total ? total : -EIO; } EXPORT_SYMBOL_GPL(tpm_get_random); diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index 01730a27ae07..507d8ab37ef1 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -120,7 +120,7 @@ static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr, num_pcrs = be32_to_cpu(cap.num_pcrs); for (i = 0; i < num_pcrs; i++) { - rc = tpm_pcr_read_dev(chip, i, digest); + rc = tpm_pcr_read(chip, i, digest); if (rc) break; str += sprintf(str, "PCR-%02d: ", i); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 44c8cad7132d..c213e09b7d81 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -49,10 +49,10 @@ struct tpm_class_ops { extern struct tpm_chip *tpm_chip_find_get(int chip_num); extern void tpm_chip_put(struct tpm_chip *chip); -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); +extern int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); +extern int tpm_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash); +extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen); +extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max); #else static inline struct tpm_chip *tpm_chip_find_get(int chip_num) { @@ -61,16 +61,16 @@ static inline struct tpm_chip *tpm_chip_find_get(int chip_num) static inline void tpm_chip_put(struct tpm_chip *chip) { } -static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) { +static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) { return -ENODEV; } -static inline int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) { +static inline int tpm_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash) { return -ENODEV; } -static inline int tpm_send(u32 chip_num, void *cmd, size_t buflen) { +static inline int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen) { return -ENODEV; } -static inline int tpm_get_random(u32 chip_num, u8 *data, size_t max) { +static inline int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max) { return -ENODEV; } #endif diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 8e4bb883fc13..8f932e53a449 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -44,8 +44,8 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; #define IMA_TEMPLATE_IMA_FMT "d|n" /* set during initialization */ +extern struct tpm_chip *ima_tpm; extern int ima_initialized; -extern int ima_used_chip; extern int ima_hash_algo; extern int ima_appraise; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index d34e7dfc1118..c4631e5bac5a 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -556,10 +556,10 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data, static void __init ima_pcrread(int idx, u8 *pcr) { - if (!ima_used_chip) + if (!ima_tpm) return; - if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0) + if (tpm_pcr_read(ima_tpm, idx, pcr) != 0) pr_err("Error Communicating to TPM chip\n"); } diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index e8f9d70a465d..bcad4da9e663 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -26,7 +26,7 @@ /* name for boot aggregate entry */ static const char *boot_aggregate_name = "boot_aggregate"; -int ima_used_chip; +struct tpm_chip *ima_tpm; /* Add the boot aggregate to the IMA measurement list and extend * the PCR register. @@ -62,7 +62,7 @@ static void __init ima_add_boot_aggregate(void) iint->ima_hash->algo = HASH_ALGO_SHA1; iint->ima_hash->length = SHA1_DIGEST_SIZE; - if (ima_used_chip) { + if (ima_tpm) { result = ima_calc_boot_aggregate(&hash.hdr); if (result < 0) { audit_cause = "hashing_error"; @@ -90,12 +90,17 @@ int __init ima_init(void) u8 pcr_i[TPM_DIGEST_SIZE]; int rc; - ima_used_chip = 0; - rc = tpm_pcr_read(TPM_ANY_NUM, 0, pcr_i); - if (rc == 0) - ima_used_chip = 1; + ima_tpm = tpm_chip_find_get(TPM_ANY_NUM); - if (!ima_used_chip) + if (ima_tpm) { + rc = tpm_pcr_read(ima_tpm, 0, pcr_i); + if (rc != 0) { + tpm_chip_put(ima_tpm); + ima_tpm = NULL; + } + } + + if (!ima_tpm) pr_info("No TPM chip found, activating TPM-bypass!\n"); rc = ima_init_crypto(); diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 552705d5a78d..83629075375c 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -93,10 +93,10 @@ static int ima_pcr_extend(const u8 *hash) { int result = 0; - if (!ima_used_chip) + if (!ima_tpm) return result; - result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash); + result = tpm_pcr_extend(ima_tpm, CONFIG_IMA_MEASURE_PCR_IDX, hash); if (result != 0) pr_err("Error Communicating to TPM chip, result: %d\n", result); return result; diff --git a/security/keys/trusted.c b/security/keys/trusted.c index c0594cb07ada..adb0caa5c38d 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -354,13 +354,13 @@ out: * For key specific tpm requests, we will generate and send our * own TPM command packets using the drivers send function. */ -static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd, +static int trusted_tpm_send(struct tpm_chip *chip, unsigned char *cmd, size_t buflen) { int rc; dump_tpm_buf(cmd); - rc = tpm_send(chip_num, cmd, buflen); + rc = tpm_send(chip, cmd, buflen); dump_tpm_buf(cmd); if (rc > 0) /* Can't return positive return codes values to keyctl */ @@ -374,30 +374,31 @@ static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd, * Prevents a trusted key that is sealed to PCRs from being accessed. * This uses the tpm driver's extend function. */ -static int pcrlock(const int pcrnum) +static int pcrlock(struct tpm_chip *chip, const int pcrnum) { unsigned char hash[SHA1_DIGEST_SIZE]; int ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE); + ret = tpm_get_random(chip, hash, SHA1_DIGEST_SIZE); if (ret != SHA1_DIGEST_SIZE) return ret; - return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0; + return tpm_pcr_extend(chip, pcrnum, hash) ? -EINVAL : 0; } /* * Create an object specific authorisation protocol (OSAP) session */ -static int osap(struct tpm_buf *tb, struct osapsess *s, +static int osap(struct tpm_chip *chip, + struct tpm_buf *tb, struct osapsess *s, const unsigned char *key, uint16_t type, uint32_t handle) { unsigned char enonce[TPM_NONCE_SIZE]; unsigned char ononce[TPM_NONCE_SIZE]; int ret; - ret = tpm_get_random(TPM_ANY_NUM, ononce, TPM_NONCE_SIZE); + ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) return ret; @@ -409,7 +410,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s, store32(tb, handle); storebytes(tb, ononce, TPM_NONCE_SIZE); - ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE); if (ret < 0) return ret; @@ -425,7 +426,8 @@ static int osap(struct tpm_buf *tb, struct osapsess *s, /* * Create an object independent authorisation protocol (oiap) session */ -static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce) +static int oiap(struct tpm_chip *chip, struct tpm_buf *tb, uint32_t *handle, + unsigned char *nonce) { int ret; @@ -433,7 +435,7 @@ static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce) store16(tb, TPM_TAG_RQU_COMMAND); store32(tb, TPM_OIAP_SIZE); store32(tb, TPM_ORD_OIAP); - ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE); if (ret < 0) return ret; @@ -455,7 +457,8 @@ struct tpm_digests { * Have the TPM seal(encrypt) the trusted key, possibly based on * Platform Configuration Registers (PCRs). AUTH1 for sealing key. */ -static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, +static int tpm_seal(struct tpm_chip *chip, + struct tpm_buf *tb, uint16_t keytype, uint32_t keyhandle, const unsigned char *keyauth, const unsigned char *data, uint32_t datalen, unsigned char *blob, uint32_t *bloblen, @@ -480,7 +483,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, return -ENOMEM; /* get session for sealing key */ - ret = osap(tb, &sess, keyauth, keytype, keyhandle); + ret = osap(chip, tb, &sess, keyauth, keytype, keyhandle); if (ret < 0) goto out; dump_sess(&sess); @@ -492,7 +495,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, if (ret < 0) goto out; - ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE); + ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) goto out; ordinal = htonl(TPM_ORD_SEAL); @@ -541,7 +544,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, store8(tb, cont); storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE); - ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE); if (ret < 0) goto out; @@ -570,7 +573,7 @@ out: /* * use the AUTH2_COMMAND form of unseal, to authorize both key and blob */ -static int tpm_unseal(struct tpm_buf *tb, +static int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, uint32_t keyhandle, const unsigned char *keyauth, const unsigned char *blob, int bloblen, const unsigned char *blobauth, @@ -589,12 +592,12 @@ static int tpm_unseal(struct tpm_buf *tb, int ret; /* sessions for unsealing key and data */ - ret = oiap(tb, &authhandle1, enonce1); + ret = oiap(chip, tb, &authhandle1, enonce1); if (ret < 0) { pr_info("trusted_key: oiap failed (%d)\n", ret); return ret; } - ret = oiap(tb, &authhandle2, enonce2); + ret = oiap(chip, tb, &authhandle2, enonce2); if (ret < 0) { pr_info("trusted_key: oiap failed (%d)\n", ret); return ret; @@ -602,7 +605,7 @@ static int tpm_unseal(struct tpm_buf *tb, ordinal = htonl(TPM_ORD_UNSEAL); keyhndl = htonl(SRKHANDLE); - ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE); + ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) { pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); return ret; @@ -634,7 +637,7 @@ static int tpm_unseal(struct tpm_buf *tb, store8(tb, cont); storebytes(tb, authdata2, SHA1_DIGEST_SIZE); - ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE); if (ret < 0) { pr_info("trusted_key: authhmac failed (%d)\n", ret); return ret; @@ -658,7 +661,8 @@ static int tpm_unseal(struct tpm_buf *tb, /* * Have the TPM seal(encrypt) the symmetric key */ -static int key_seal(struct trusted_key_payload *p, +static int key_seal(struct tpm_chip *chip, + struct trusted_key_payload *p, struct trusted_key_options *o) { struct tpm_buf *tb; @@ -671,7 +675,7 @@ static int key_seal(struct trusted_key_payload *p, /* include migratable flag at end of sealed key */ p->key[p->key_len] = p->migratable; - ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth, + ret = tpm_seal(chip, tb, o->keytype, o->keyhandle, o->keyauth, p->key, p->key_len + 1, p->blob, &p->blob_len, o->blobauth, o->pcrinfo, o->pcrinfo_len); if (ret < 0) @@ -684,7 +688,8 @@ static int key_seal(struct trusted_key_payload *p, /* * Have the TPM unseal(decrypt) the symmetric key */ -static int key_unseal(struct trusted_key_payload *p, +static int key_unseal(struct tpm_chip *chip, + struct trusted_key_payload *p, struct trusted_key_options *o) { struct tpm_buf *tb; @@ -694,7 +699,8 @@ static int key_unseal(struct trusted_key_payload *p, if (!tb) return -ENOMEM; - ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len, + ret = tpm_unseal(chip, tb, o->keyhandle, o->keyauth, + p->blob, p->blob_len, o->blobauth, p->key, &p->key_len); if (ret < 0) pr_info("trusted_key: srkunseal failed (%d)\n", ret); @@ -900,6 +906,7 @@ static int trusted_instantiate(struct key *key, { struct trusted_key_payload *payload = NULL; struct trusted_key_options *options = NULL; + struct tpm_chip *chip = NULL; size_t datalen = prep->datalen; char *datablob; int ret = 0; @@ -935,9 +942,14 @@ static int trusted_instantiate(struct key *key, dump_payload(payload); dump_options(options); + ret = -ENODEV; + chip = tpm_chip_find_get(TPM_ANY_NUM); + if (!chip) + goto out; + switch (key_cmd) { case Opt_load: - ret = key_unseal(payload, options); + ret = key_unseal(chip, payload, options); dump_payload(payload); dump_options(options); if (ret < 0) @@ -945,12 +957,12 @@ static int trusted_instantiate(struct key *key, break; case Opt_new: key_len = payload->key_len; - ret = tpm_get_random(TPM_ANY_NUM, payload->key, key_len); + ret = tpm_get_random(chip, payload->key, key_len); if (ret != key_len) { pr_info("trusted_key: key_create failed (%d)\n", ret); goto out; } - ret = key_seal(payload, options); + ret = key_seal(chip, payload, options); if (ret < 0) pr_info("trusted_key: key_seal failed (%d)\n", ret); break; @@ -958,9 +970,11 @@ static int trusted_instantiate(struct key *key, ret = -EINVAL; goto out; } + if (!ret && options->pcrlock) - ret = pcrlock(options->pcrlock); + ret = pcrlock(chip, options->pcrlock); out: + tpm_chip_put(chip); kfree(datablob); kfree(options); if (!ret) @@ -987,6 +1001,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) struct trusted_key_payload *p = key->payload.data; struct trusted_key_payload *new_p; struct trusted_key_options *new_o; + struct tpm_chip *chip = NULL; size_t datalen = prep->datalen; char *datablob; int ret = 0; @@ -1018,6 +1033,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) kfree(new_p); goto out; } + /* copy old key values, and reseal with new pcrs */ new_p->migratable = p->migratable; new_p->key_len = p->key_len; @@ -1025,14 +1041,19 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) dump_payload(p); dump_payload(new_p); - ret = key_seal(new_p, new_o); + ret = -ENODEV; + chip = tpm_chip_find_get(TPM_ANY_NUM); + if (!chip) + goto out; + + ret = key_seal(chip, new_p, new_o); if (ret < 0) { pr_info("trusted_key: key_seal failed (%d)\n", ret); kfree(new_p); goto out; } if (new_o->pcrlock) { - ret = pcrlock(new_o->pcrlock); + ret = pcrlock(chip, new_o->pcrlock); if (ret < 0) { pr_info("trusted_key: pcrlock failed (%d)\n", ret); kfree(new_p); @@ -1042,6 +1063,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) rcu_assign_keypointer(key, new_p); call_rcu(&p->rcu, trusted_rcu_free); out: + tpm_chip_put(chip); kfree(datablob); kfree(new_o); return ret; |
From: Jarkko S. <jar...@li...> - 2018-08-24 07:42:32
|
On Tue, Aug 21, 2018 at 04:57:22PM +0100, David Howells wrote: > Use struct tpm_chip rather than chip number as interface parameter for most > interface functions. This allows the client to be sure about the consistency > of what device it uses. > > Signed-off-by: David Howells <dho...@re...> > --- > > drivers/char/tpm/tpm-interface.c | 76 ++++++++------------------------- > drivers/char/tpm/tpm-sysfs.c | 2 - > include/linux/tpm.h | 16 ++++--- > security/integrity/ima/ima.h | 2 - > security/integrity/ima/ima_crypto.c | 4 +- > security/integrity/ima/ima_init.c | 19 +++++--- > security/integrity/ima/ima_queue.c | 4 +- > security/keys/trusted.c | 80 ++++++++++++++++++++++------------- > 8 files changed, 96 insertions(+), 107 deletions(-) Should be split at least to three patches and TPM side is already in the upstream. /Jarkko |
From: David H. <dho...@re...> - 2018-08-21 15:57:38
|
Move the ordinal values (command IDs) from the tpm-interface.c file to the tpm_command.h header where the other ordinal values are declared. Use cpu_to_be32() when the ordinal values are used, and not in their declarations. The TPM_TAG_RQU_COMMAND definition in the internal tpm.h has to be removed because it is a differently-defined duplicate label to avoid a clash. cpu_to_be16() is then used in the places where it was used. This allows the infineon TPM driver to use the constants directly in tpm_inf_pnp_suspend(). The savestate buffer there can also be made static and const. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-interface.c | 48 +++++++++++++++++--------------------- drivers/char/tpm/tpm-sysfs.c | 8 +++--- drivers/char/tpm/tpm.h | 2 -- drivers/char/tpm/tpm_infineon.c | 6 ++--- include/linux/tpm_command.h | 19 +++++++++++---- 5 files changed, 42 insertions(+), 41 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 29c2ce5cfc69..9add6034c252 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -28,6 +28,7 @@ #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/freezer.h> +#include <linux/tpm_command.h> #include "tpm.h" #include "tpm_eventlog.h" @@ -417,13 +418,11 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, } #define TPM_INTERNAL_RESULT_SIZE 200 -#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, - .length = cpu_to_be32(22), - .ordinal = TPM_ORD_GET_CAP + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .length = cpu_to_be32(22), + .ordinal = cpu_to_be32(TPM_ORD_GET_CAP), }; ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap, @@ -469,14 +468,13 @@ void tpm_gen_interrupt(struct tpm_chip *chip) } EXPORT_SYMBOL_GPL(tpm_gen_interrupt); -#define TPM_ORD_STARTUP cpu_to_be32(153) #define TPM_ST_CLEAR cpu_to_be16(1) #define TPM_ST_STATE cpu_to_be16(2) #define TPM_ST_DEACTIVATED cpu_to_be16(3) static const struct tpm_input_header tpm_startup_header = { - .tag = TPM_TAG_RQU_COMMAND, - .length = cpu_to_be32(12), - .ordinal = TPM_ORD_STARTUP + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .length = cpu_to_be32(12), + .ordinal = cpu_to_be32(TPM_ORD_STARTUP), }; static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) @@ -609,12 +607,11 @@ duration: } EXPORT_SYMBOL_GPL(tpm_get_timeouts); -#define TPM_ORD_CONTINUE_SELFTEST 83 #define CONTINUE_SELFTEST_RESULT_SIZE 10 static struct tpm_input_header continue_selftest_header = { - .tag = TPM_TAG_RQU_COMMAND, - .length = cpu_to_be32(10), + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .length = cpu_to_be32(10), .ordinal = cpu_to_be32(TPM_ORD_CONTINUE_SELFTEST), }; @@ -670,12 +667,11 @@ void tpm_chip_put(struct tpm_chip *chip) } EXPORT_SYMBOL_GPL(tpm_chip_put); -#define TPM_ORDINAL_PCRREAD cpu_to_be32(21) #define READ_PCR_RESULT_SIZE 30 static struct tpm_input_header pcrread_header = { - .tag = TPM_TAG_RQU_COMMAND, - .length = cpu_to_be32(14), - .ordinal = TPM_ORDINAL_PCRREAD + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .length = cpu_to_be32(14), + .ordinal = cpu_to_be32(TPM_ORD_PCR_READ), }; /** @@ -716,12 +712,11 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); * isn't, protect against the chip disappearing, by incrementing * the module usage count. */ -#define TPM_ORD_PCR_EXTEND cpu_to_be32(20) #define EXTEND_PCR_RESULT_SIZE 34 static struct tpm_input_header pcrextend_header = { - .tag = TPM_TAG_RQU_COMMAND, - .length = cpu_to_be32(34), - .ordinal = TPM_ORD_PCR_EXTEND + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .length = cpu_to_be32(34), + .ordinal = cpu_to_be32(TPM_ORD_PCR_EXTEND) }; int tpm_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash) @@ -889,13 +884,12 @@ void tpm_remove_hardware(struct device *dev) } EXPORT_SYMBOL_GPL(tpm_remove_hardware); -#define TPM_ORD_SAVESTATE cpu_to_be32(152) #define SAVESTATE_RESULT_SIZE 10 static struct tpm_input_header savestate_header = { - .tag = TPM_TAG_RQU_COMMAND, - .length = cpu_to_be32(10), - .ordinal = TPM_ORD_SAVESTATE + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .length = cpu_to_be32(10), + .ordinal = cpu_to_be32(TPM_ORD_SAVESTATE), }; /* @@ -971,9 +965,9 @@ 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 + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .length = cpu_to_be32(14), + .ordinal = cpu_to_be32(TPM_ORD_GET_RANDOM), }; /** diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index 507d8ab37ef1..d8da83a1d11c 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -18,6 +18,7 @@ * */ #include <linux/device.h> +#include <linux/tpm_command.h> #include "tpm.h" /* XXX for now this helper is duplicated in tpm-interface.c */ @@ -40,11 +41,10 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, } #define READ_PUBEK_RESULT_SIZE 314 -#define TPM_ORD_READPUBEK cpu_to_be32(124) static struct tpm_input_header tpm_readpubek_header = { - .tag = TPM_TAG_RQU_COMMAND, - .length = cpu_to_be32(30), - .ordinal = TPM_ORD_READPUBEK + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .length = cpu_to_be32(30), + .ordinal = cpu_to_be32(TPM_ORD_READPUBEK), }; static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index df6ffceb3429..2a1be0ec2fbd 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -138,8 +138,6 @@ struct tpm_output_header { __be32 return_code; } __packed; -#define TPM_TAG_RQU_COMMAND cpu_to_be16(193) - struct stclear_flags_t { __be16 tag; u8 deactivated; diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index dc0a2554034e..7daa3317e18d 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -591,10 +591,10 @@ static int tpm_inf_pnp_suspend(struct pnp_dev *dev, pm_message_t pm_state) struct tpm_chip *chip = pnp_get_drvdata(dev); int rc; if (chip) { - u8 savestate[] = { - 0, 193, /* TPM_TAG_RQU_COMMAND */ + static const u8 savestate[] = { + 0, TPM_TAG_RQU_COMMAND, 0, 0, 0, 10, /* blob length (in bytes) */ - 0, 0, 0, 152 /* TPM_ORD_SaveState */ + 0, 0, 0, TPM_ORD_SAVESTATE, }; dev_info(&dev->dev, "saving TPM state\n"); rc = tpm_inf_send(chip, savestate, sizeof(savestate)); diff --git a/include/linux/tpm_command.h b/include/linux/tpm_command.h index 727512e249b5..a3e0bb670e62 100644 --- a/include/linux/tpm_command.h +++ b/include/linux/tpm_command.h @@ -15,11 +15,20 @@ #define TPM_TAG_RSP_AUTH2_COMMAND 198 /* Command Ordinals */ -#define TPM_ORD_GETRANDOM 70 -#define TPM_ORD_OSAP 11 -#define TPM_ORD_OIAP 10 -#define TPM_ORD_SEAL 23 -#define TPM_ORD_UNSEAL 24 +enum tpm_ordinal { + TPM_ORD_OSAP = 11, + TPM_ORD_OIAP = 10, + TPM_ORD_PCR_EXTEND = 20, + TPM_ORD_PCR_READ = 21, + TPM_ORD_SEAL = 23, + TPM_ORD_UNSEAL = 24, + TPM_ORD_GET_RANDOM = 70, + TPM_ORD_CONTINUE_SELFTEST = 83, + TPM_ORD_GET_CAP = 101, + TPM_ORD_READPUBEK = 124, + TPM_ORD_SAVESTATE = 152, + TPM_ORD_STARTUP = 153, +}; /* Other constants */ #define SRKHANDLE 0x40000000 |
From: David H. <dho...@re...> - 2018-08-21 15:57:45
|
--- drivers/char/tpm/tpm-dev.c | 17 ++-- drivers/char/tpm/tpm-interface.c | 171 +++++++++++++++++++------------------- drivers/char/tpm/tpm-sysfs.c | 23 ----- drivers/char/tpm/tpm.h | 8 +- include/linux/tpm.h | 7 +- security/keys/trusted.c | 16 ++-- 6 files changed, 117 insertions(+), 125 deletions(-) diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index d9b774e02a1f..6809c2791276 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -32,7 +32,10 @@ struct file_priv { struct timer_list user_read_timer; /* user needs to claim result */ struct work_struct work; - u8 data_buffer[TPM_BUFSIZE]; + union { + u8 data_buffer[TPM_BUFSIZE]; + struct tpm_output_header reply; + }; }; static void user_reader_timeout(unsigned long ptr) @@ -119,7 +122,7 @@ static ssize_t tpm_write(struct file *file, const char __user *buf, { struct file_priv *priv = file->private_data; size_t in_size = size; - ssize_t out_size; + long rc; /* cannot perform a write until the read has cleared either via tpm_read or a user_read_timer timeout. @@ -140,14 +143,14 @@ static ssize_t tpm_write(struct file *file, const char __user *buf, } /* atomic tpm command send and result receive */ - out_size = tpm_transmit(priv->chip, priv->data_buffer, - sizeof(priv->data_buffer)); - if (out_size < 0) { + rc = tpm_send_command(priv->chip, priv->data_buffer, + sizeof(priv->data_buffer), NULL); + if (rc < 0) { mutex_unlock(&priv->buffer_mutex); - return out_size; + return rc; } - atomic_set(&priv->data_pending, out_size); + atomic_set(&priv->data_pending, be32_to_cpu(priv->reply.length)); mutex_unlock(&priv->buffer_mutex); /* Set a timeout by which the reader must come claim the result */ diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 9add6034c252..e90f9d2dfaf2 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -329,13 +329,34 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); -/* - * Internal kernel interface to transmit TPM commands +/** + * tpm_send_command - Send a command to the TPM and receive the reply + * @chip: The TPM to communicate with + * @buf: The command and reply buffer + * @bufsiz: The maximum amount of space in buffer for the reply + * @desc: Info about the command being performed for printing purposes (or NULL) + * + * This function sends a command to the TPM and then receives the reply. The + * command must be in the buffer on entry, with the length of the command + * indicated by the command header in the buffer. + * + * The reply is read into the buffer, overwriting the command, up to a maximum + * length of bufsiz. + * + * If the TPM reports an error, desc is used to fabricate an error message. + * + * This function returns 0 on success, a negative kernel error code or a + * positive TPM error code on failure. + * + * In the case that success or a TPM error code is returned, the buffer is + * guaranteed to contain at least a valid reply header. The length of the + * reply is contained in the reply header. */ -ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, - size_t bufsiz) +long tpm_send_command(struct tpm_chip *chip, void *buf, size_t bufsiz, + const char *desc) { - ssize_t rc; + struct tpm_output_header *reply; + long rc; u32 count, ordinal; unsigned long stop; @@ -393,29 +414,30 @@ out_recv: "tpm_transmit: tpm_recv: error %zd\n", rc); out: mutex_unlock(&chip->tpm_mutex); + if (rc < 0) + return rc; + + /* The transmission apparently worked. Sanity check the reply and + * extract the return code. + */ + if (rc < TPM_HEADER_SIZE) + return -EIO; + reply = buf; + + rc = be32_to_cpu(reply->length); + if (rc < TPM_HEADER_SIZE || rc > bufsiz) + return -EIO; + + rc = be32_to_cpu(reply->return_code); + if (rc < 0 || rc >= 0x1000) + return -EIO; + if (rc != 0 && desc) + dev_err(chip->dev, + "A TPM error (%ld) occurred %s\n", rc, desc); return rc; } #define TPM_DIGEST_SIZE 20 -#define TPM_RET_CODE_IDX 6 - -static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, - int len, const char *desc) -{ - int err; - - len = tpm_transmit(chip, (u8 *) cmd, len); - if (len < 0) - return len; - else if (len < TPM_HEADER_SIZE) - return -EFAULT; - - err = be32_to_cpu(cmd->header.out.return_code); - if (err != 0 && desc) - dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); - - return err; -} #define TPM_INTERNAL_RESULT_SIZE 200 @@ -425,8 +447,8 @@ static const struct tpm_input_header tpm_getcap_header = { .ordinal = cpu_to_be32(TPM_ORD_GET_CAP), }; -ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap, - const char *desc) +long tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap, + const char *desc) { struct tpm_cmd_t tpm_cmd; int rc; @@ -447,7 +469,7 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap, tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = subcap_id; } - rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc); + rc = tpm_send_command(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc); if (!rc) *cap = tpm_cmd.params.getcap_out.cap; return rc; @@ -456,15 +478,14 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap, void tpm_gen_interrupt(struct tpm_chip *chip) { struct tpm_cmd_t tpm_cmd; - ssize_t rc; tpm_cmd.header.in = tpm_getcap_header; tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; - rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, - "attempting to determine the timeouts"); + tpm_send_command(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + "attempting to determine the timeouts"); } EXPORT_SYMBOL_GPL(tpm_gen_interrupt); @@ -477,16 +498,16 @@ static const struct tpm_input_header tpm_startup_header = { .ordinal = cpu_to_be32(TPM_ORD_STARTUP), }; -static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) +static long tpm_startup(struct tpm_chip *chip, struct tpm_cmd_t *start_cmd, + __be16 startup_type) { - struct tpm_cmd_t start_cmd; - start_cmd.header.in = tpm_startup_header; - start_cmd.params.startup_in.startup_type = startup_type; - return transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, - "attempting to start the TPM"); + start_cmd->header.in = tpm_startup_header; + start_cmd->params.startup_in.startup_type = startup_type; + return tpm_send_command(chip, start_cmd, TPM_INTERNAL_RESULT_SIZE, + "attempting to start the TPM"); } -int tpm_get_timeouts(struct tpm_chip *chip) +long tpm_get_timeouts(struct tpm_chip *chip) { struct tpm_cmd_t tpm_cmd; unsigned long new_timeout[4]; @@ -498,32 +519,28 @@ int tpm_get_timeouts(struct tpm_chip *chip) tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; - rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL); + rc = tpm_send_command(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + "attempting to determine the timeouts"); if (rc == TPM_ERR_INVALID_POSTINIT) { /* The TPM is not started, we are the first to talk to it. Execute a startup command. */ dev_info(chip->dev, "Issuing TPM_STARTUP"); - if (tpm_startup(chip, TPM_ST_CLEAR)) + if (tpm_startup(chip, &tpm_cmd, TPM_ST_CLEAR)) return rc; tpm_cmd.header.in = tpm_getcap_header; tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; - rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, - NULL); + rc = tpm_send_command(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + "attempting to determine the timeouts"); } - if (rc) { - dev_err(chip->dev, - "A TPM error (%zd) occurred attempting to determine the timeouts\n", - rc); + if (rc) goto duration; - } - if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || - be32_to_cpu(tpm_cmd.header.out.length) - != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) + if (be32_to_cpu(tpm_cmd.header.out.length) != + sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) return -EINVAL; old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a); @@ -573,8 +590,8 @@ duration: tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION; - rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, - "attempting to determine the durations"); + rc = tpm_send_command(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + "attempting to determine the durations"); if (rc) return rc; @@ -622,15 +639,11 @@ static struct tpm_input_header continue_selftest_header = { * Returns 0 on success, < 0 in case of fatal error or a value > 0 representing * a TPM error code. */ -static int tpm_continue_selftest(struct tpm_chip *chip) +static int tpm_continue_selftest(struct tpm_chip *chip, struct tpm_cmd_t *cmd) { - int rc; - struct tpm_cmd_t cmd; - - cmd.header.in = continue_selftest_header; - rc = transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, - "continue selftest"); - return rc; + cmd->header.in = continue_selftest_header; + return tpm_send_command(chip, cmd, CONTINUE_SELFTEST_RESULT_SIZE, + "continue selftest"); } /** @@ -692,8 +705,8 @@ int tpm_pcr_read(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 = transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, - "attempting to read a pcr value"); + rc = tpm_send_command(chip, &cmd, READ_PCR_RESULT_SIZE, + "attempting to read a pcr value"); if (rc == 0) memcpy(res_buf, cmd.params.pcrread_out.pcr_result, @@ -726,8 +739,8 @@ int tpm_pcr_extend(struct tpm_chip *chip, 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); - return transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, - "attempting extend a PCR value"); + return tpm_send_command(chip, &cmd, EXTEND_PCR_RESULT_SIZE, + "attempting extend a PCR value"); } EXPORT_SYMBOL_GPL(tpm_pcr_extend); @@ -739,9 +752,9 @@ EXPORT_SYMBOL_GPL(tpm_pcr_extend); * Returns 0 on success, < 0 in case of fatal error or a value > 0 representing * a TPM error code. */ -int tpm_do_selftest(struct tpm_chip *chip) +long tpm_do_selftest(struct tpm_chip *chip) { - int rc; + long rc; unsigned int loops; unsigned int delay_msec = 100; unsigned long duration; @@ -751,7 +764,7 @@ int tpm_do_selftest(struct tpm_chip *chip) loops = jiffies_to_msecs(duration) / delay_msec; - rc = tpm_continue_selftest(chip); + rc = tpm_continue_selftest(chip, &cmd); /* This may fail if there was no TPM driver during a suspend/resume * cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST) */ @@ -762,7 +775,7 @@ int tpm_do_selftest(struct tpm_chip *chip) /* Attempt to read a PCR value */ cmd.header.in = pcrread_header; cmd.params.pcrread_in.pcr_idx = cpu_to_be32(0); - rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE); + rc = tpm_send_command(chip, &cmd, READ_PCR_RESULT_SIZE, NULL); /* Some buggy TPMs will not respond to tpm_tis_ready() for * around 300ms while the self test is ongoing, keep trying * until the self test duration expires. */ @@ -772,13 +785,9 @@ int tpm_do_selftest(struct tpm_chip *chip) continue; } - if (rc < TPM_HEADER_SIZE) - return -EFAULT; - - rc = be32_to_cpu(cmd.header.out.return_code); if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) { dev_info(chip->dev, - "TPM is disabled/deactivated (0x%X)\n", rc); + "TPM is disabled/deactivated (0x%lX)\n", rc); /* TPM is disabled and/or deactivated; driver can * proceed and TPM does handle commands for * suspend/resume correctly @@ -794,12 +803,6 @@ int tpm_do_selftest(struct tpm_chip *chip) } EXPORT_SYMBOL_GPL(tpm_do_selftest); -int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen) -{ - return transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd"); -} -EXPORT_SYMBOL_GPL(tpm_send); - static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, bool check_cancel, bool *canceled) { @@ -913,14 +916,14 @@ 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 = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, - "extending dummy pcr before suspend"); + tpm_send_command(chip, &cmd, EXTEND_PCR_RESULT_SIZE, + "extending dummy pcr before suspend"); } /* now do the actual savestate */ for (try = 0; try < TPM_RETRY; try++) { cmd.header.in = savestate_header; - rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, NULL); + rc = tpm_send_command(chip, &cmd, SAVESTATE_RESULT_SIZE, NULL); /* * If the TPM indicates that it is too busy to respond to @@ -992,9 +995,9 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) 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"); + err = tpm_send_command(chip, &tpm_cmd, + TPM_GETRANDOM_RESULT_SIZE + num_bytes, + "attempting get random"); if (err) break; diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index d8da83a1d11c..ad3b01882b15 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -21,25 +21,6 @@ #include <linux/tpm_command.h> #include "tpm.h" -/* XXX for now this helper is duplicated in tpm-interface.c */ -static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, - int len, const char *desc) -{ - int err; - - len = tpm_transmit(chip, (u8 *) cmd, len); - if (len < 0) - return len; - else if (len < TPM_HEADER_SIZE) - return -EFAULT; - - err = be32_to_cpu(cmd->header.out.return_code); - if (err != 0 && desc) - dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); - - return err; -} - #define READ_PUBEK_RESULT_SIZE 314 static struct tpm_input_header tpm_readpubek_header = { .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), @@ -58,8 +39,8 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, struct tpm_chip *chip = dev_get_drvdata(dev); tpm_cmd.header.in = tpm_readpubek_header; - err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, - "attempting to read the PUBEK"); + err = tpm_send_command(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, + "attempting to read the PUBEK"); if (err) goto out; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 2a1be0ec2fbd..912eba092e62 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -306,13 +306,11 @@ struct tpm_cmd_t { tpm_cmd_params params; } __packed; -ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *); +extern long tpm_getcap(struct device *, __be32, cap_t *, const char *); -ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, - size_t bufsiz); -extern int tpm_get_timeouts(struct tpm_chip *); +extern long tpm_get_timeouts(struct tpm_chip *); extern void tpm_gen_interrupt(struct tpm_chip *); -extern int tpm_do_selftest(struct tpm_chip *); +extern long tpm_do_selftest(struct tpm_chip *); extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32); extern struct tpm_chip* tpm_register_hardware(struct device *, const struct tpm_class_ops *ops); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index c213e09b7d81..f4e14405f5cf 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -51,7 +51,8 @@ extern void tpm_chip_put(struct tpm_chip *chip); extern int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); extern int tpm_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash); -extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen); +extern long tpm_send_command(struct tpm_chip *chip, void *buf, size_t buflen, + const char *desc); extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max); #else static inline struct tpm_chip *tpm_chip_find_get(int chip_num) @@ -67,7 +68,9 @@ static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) static inline int tpm_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash) { return -ENODEV; } -static inline int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen) { +static inline long tpm_send_command(struct tpm_chip *chip, void *buf, size_t buflen, + const char *desc) +{ return -ENODEV; } static inline int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max) { diff --git a/security/keys/trusted.c b/security/keys/trusted.c index adb0caa5c38d..943c65b53201 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -355,12 +355,12 @@ out: * own TPM command packets using the drivers send function. */ static int trusted_tpm_send(struct tpm_chip *chip, unsigned char *cmd, - size_t buflen) + size_t buflen, const char *desc) { int rc; dump_tpm_buf(cmd); - rc = tpm_send(chip, cmd, buflen); + rc = tpm_send_command(chip, cmd, buflen, desc); dump_tpm_buf(cmd); if (rc > 0) /* Can't return positive return codes values to keyctl */ @@ -410,7 +410,8 @@ static int osap(struct tpm_chip *chip, store32(tb, handle); storebytes(tb, ononce, TPM_NONCE_SIZE); - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, + "creating OSAP session"); if (ret < 0) return ret; @@ -435,7 +436,8 @@ static int oiap(struct tpm_chip *chip, struct tpm_buf *tb, uint32_t *handle, store16(tb, TPM_TAG_RQU_COMMAND); store32(tb, TPM_OIAP_SIZE); store32(tb, TPM_ORD_OIAP); - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, + "creating OIAP session"); if (ret < 0) return ret; @@ -544,7 +546,8 @@ static int tpm_seal(struct tpm_chip *chip, store8(tb, cont); storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE); - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, + "sealing data"); if (ret < 0) goto out; @@ -637,7 +640,8 @@ static int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, store8(tb, cont); storebytes(tb, authdata2, SHA1_DIGEST_SIZE); - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, + "unsealing data"); if (ret < 0) { pr_info("trusted_key: authhmac failed (%d)\n", ret); return ret; |
From: David H. <dho...@re...> - 2018-08-21 15:57:53
|
Break the TPM bits out of security/keys/trusted.c into their own call wrapper library. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/Makefile | 2 drivers/char/tpm/tpm-library.c | 682 ++++++++++++++++++++++++++++++++++++++++ drivers/char/tpm/tpm-library.h | 83 +++++ include/linux/tpm.h | 38 ++ security/keys/trusted.c | 646 -------------------------------------- security/keys/trusted.h | 80 ----- 6 files changed, 804 insertions(+), 727 deletions(-) create mode 100644 drivers/char/tpm/tpm-library.c create mode 100644 drivers/char/tpm/tpm-library.h diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index b179052cd81b..cea6a4d05920 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -2,7 +2,7 @@ # Makefile for the kernel tpm device drivers. # obj-$(CONFIG_TCG_TPM) += tpm.o -tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o +tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-library.o tpm-$(CONFIG_ACPI) += tpm_ppi.o ifdef CONFIG_ACPI diff --git a/drivers/char/tpm/tpm-library.c b/drivers/char/tpm/tpm-library.c new file mode 100644 index 000000000000..1be4f71cabcb --- /dev/null +++ b/drivers/char/tpm/tpm-library.c @@ -0,0 +1,682 @@ +/* TPM call wrapper library. + * + * Copyright (C) 2010 IBM Corporation + * + * Author: + * David Safford <sa...@us...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * See Documentation/security/keys-trusted-encrypted.txt + */ + +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/parser.h> +#include <linux/string.h> +#include <linux/err.h> +#include <keys/user-type.h> +#include <keys/trusted-type.h> +#include <linux/key-type.h> +#include <linux/rcupdate.h> +#include <linux/crypto.h> +#include <crypto/hash.h> +#include <crypto/sha.h> +#include <linux/capability.h> +#include <linux/tpm.h> +#include <linux/tpm_command.h> + +#include "tpm-library.h" + +static const char hmac_alg[] = "hmac(sha1)"; +static const char hash_alg[] = "sha1"; + +struct sdesc { + struct shash_desc shash; + char ctx[]; +}; + +static struct crypto_shash *hashalg; +static struct crypto_shash *hmacalg; + +static struct sdesc *init_sdesc(struct crypto_shash *alg) +{ + struct sdesc *sdesc; + int size; + + size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); + sdesc = kmalloc(size, GFP_KERNEL); + if (!sdesc) + return ERR_PTR(-ENOMEM); + sdesc->shash.tfm = alg; + sdesc->shash.flags = 0x0; + return sdesc; +} + +static int TSS_sha1(const unsigned char *data, unsigned int datalen, + unsigned char *digest) +{ + struct sdesc *sdesc; + int ret; + + sdesc = init_sdesc(hashalg); + if (IS_ERR(sdesc)) { + pr_info("trusted_key: can't alloc %s\n", hash_alg); + return PTR_ERR(sdesc); + } + + ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); + kfree(sdesc); + return ret; +} + +static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, + unsigned int keylen, ...) +{ + struct sdesc *sdesc; + va_list argp; + unsigned int dlen; + unsigned char *data; + int ret; + + sdesc = init_sdesc(hmacalg); + if (IS_ERR(sdesc)) { + pr_info("trusted_key: can't alloc %s\n", hmac_alg); + return PTR_ERR(sdesc); + } + + ret = crypto_shash_setkey(hmacalg, key, keylen); + if (ret < 0) + goto out; + ret = crypto_shash_init(&sdesc->shash); + if (ret < 0) + goto out; + + va_start(argp, keylen); + for (;;) { + dlen = va_arg(argp, unsigned int); + if (dlen == 0) + break; + data = va_arg(argp, unsigned char *); + if (data == NULL) { + ret = -EINVAL; + break; + } + ret = crypto_shash_update(&sdesc->shash, data, dlen); + if (ret < 0) + break; + } + va_end(argp); + if (!ret) + ret = crypto_shash_final(&sdesc->shash, digest); +out: + kfree(sdesc); + return ret; +} + +/* + * calculate authorization info fields to send to TPM + */ +static int TSS_authhmac(unsigned char *digest, const unsigned char *key, + unsigned int keylen, unsigned char *h1, + unsigned char *h2, unsigned char h3, ...) +{ + unsigned char paramdigest[SHA1_DIGEST_SIZE]; + struct sdesc *sdesc; + unsigned int dlen; + unsigned char *data; + unsigned char c; + int ret; + va_list argp; + + sdesc = init_sdesc(hashalg); + if (IS_ERR(sdesc)) { + pr_info("trusted_key: can't alloc %s\n", hash_alg); + return PTR_ERR(sdesc); + } + + c = h3; + ret = crypto_shash_init(&sdesc->shash); + if (ret < 0) + goto out; + va_start(argp, h3); + for (;;) { + dlen = va_arg(argp, unsigned int); + if (dlen == 0) + break; + data = va_arg(argp, unsigned char *); + if (!data) { + ret = -EINVAL; + break; + } + ret = crypto_shash_update(&sdesc->shash, data, dlen); + if (ret < 0) + break; + } + va_end(argp); + if (!ret) + ret = crypto_shash_final(&sdesc->shash, paramdigest); + if (!ret) + ret = TSS_rawhmac(digest, key, keylen, SHA1_DIGEST_SIZE, + paramdigest, TPM_NONCE_SIZE, h1, + TPM_NONCE_SIZE, h2, 1, &c, 0, 0); +out: + kfree(sdesc); + return ret; +} + +/* + * verify the AUTH1_COMMAND (Seal) result from TPM + */ +static int TSS_checkhmac1(unsigned char *buffer, + const uint32_t command, + const unsigned char *ononce, + const unsigned char *key, + unsigned int keylen, ...) +{ + uint32_t bufsize; + uint16_t tag; + uint32_t ordinal; + uint32_t result; + unsigned char *enonce; + unsigned char *continueflag; + unsigned char *authdata; + unsigned char testhmac[SHA1_DIGEST_SIZE]; + unsigned char paramdigest[SHA1_DIGEST_SIZE]; + struct sdesc *sdesc; + unsigned int dlen; + unsigned int dpos; + va_list argp; + int ret; + + bufsize = LOAD32(buffer, TPM_SIZE_OFFSET); + tag = LOAD16(buffer, 0); + ordinal = command; + result = LOAD32N(buffer, TPM_RETURN_OFFSET); + if (tag == TPM_TAG_RSP_COMMAND) + return 0; + if (tag != TPM_TAG_RSP_AUTH1_COMMAND) + return -EINVAL; + authdata = buffer + bufsize - SHA1_DIGEST_SIZE; + continueflag = authdata - 1; + enonce = continueflag - TPM_NONCE_SIZE; + + sdesc = init_sdesc(hashalg); + if (IS_ERR(sdesc)) { + pr_info("trusted_key: can't alloc %s\n", hash_alg); + return PTR_ERR(sdesc); + } + ret = crypto_shash_init(&sdesc->shash); + if (ret < 0) + goto out; + ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, + sizeof result); + if (ret < 0) + goto out; + ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, + sizeof ordinal); + if (ret < 0) + goto out; + va_start(argp, keylen); + for (;;) { + dlen = va_arg(argp, unsigned int); + if (dlen == 0) + break; + dpos = va_arg(argp, unsigned int); + ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); + if (ret < 0) + break; + } + va_end(argp); + if (!ret) + ret = crypto_shash_final(&sdesc->shash, paramdigest); + if (ret < 0) + goto out; + + ret = TSS_rawhmac(testhmac, key, keylen, SHA1_DIGEST_SIZE, paramdigest, + TPM_NONCE_SIZE, enonce, TPM_NONCE_SIZE, ononce, + 1, continueflag, 0, 0); + if (ret < 0) + goto out; + + if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE)) + ret = -EINVAL; +out: + kfree(sdesc); + return ret; +} + +/* + * verify the AUTH2_COMMAND (unseal) result from TPM + */ +static int TSS_checkhmac2(unsigned char *buffer, + const uint32_t command, + const unsigned char *ononce, + const unsigned char *key1, + unsigned int keylen1, + const unsigned char *key2, + unsigned int keylen2, ...) +{ + uint32_t bufsize; + uint16_t tag; + uint32_t ordinal; + uint32_t result; + unsigned char *enonce1; + unsigned char *continueflag1; + unsigned char *authdata1; + unsigned char *enonce2; + unsigned char *continueflag2; + unsigned char *authdata2; + unsigned char testhmac1[SHA1_DIGEST_SIZE]; + unsigned char testhmac2[SHA1_DIGEST_SIZE]; + unsigned char paramdigest[SHA1_DIGEST_SIZE]; + struct sdesc *sdesc; + unsigned int dlen; + unsigned int dpos; + va_list argp; + int ret; + + bufsize = LOAD32(buffer, TPM_SIZE_OFFSET); + tag = LOAD16(buffer, 0); + ordinal = command; + result = LOAD32N(buffer, TPM_RETURN_OFFSET); + + if (tag == TPM_TAG_RSP_COMMAND) + return 0; + if (tag != TPM_TAG_RSP_AUTH2_COMMAND) + return -EINVAL; + authdata1 = buffer + bufsize - (SHA1_DIGEST_SIZE + 1 + + SHA1_DIGEST_SIZE + SHA1_DIGEST_SIZE); + authdata2 = buffer + bufsize - (SHA1_DIGEST_SIZE); + continueflag1 = authdata1 - 1; + continueflag2 = authdata2 - 1; + enonce1 = continueflag1 - TPM_NONCE_SIZE; + enonce2 = continueflag2 - TPM_NONCE_SIZE; + + sdesc = init_sdesc(hashalg); + if (IS_ERR(sdesc)) { + pr_info("trusted_key: can't alloc %s\n", hash_alg); + return PTR_ERR(sdesc); + } + ret = crypto_shash_init(&sdesc->shash); + if (ret < 0) + goto out; + ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, + sizeof result); + if (ret < 0) + goto out; + ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, + sizeof ordinal); + if (ret < 0) + goto out; + + va_start(argp, keylen2); + for (;;) { + dlen = va_arg(argp, unsigned int); + if (dlen == 0) + break; + dpos = va_arg(argp, unsigned int); + ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); + if (ret < 0) + break; + } + va_end(argp); + if (!ret) + ret = crypto_shash_final(&sdesc->shash, paramdigest); + if (ret < 0) + goto out; + + ret = TSS_rawhmac(testhmac1, key1, keylen1, SHA1_DIGEST_SIZE, + paramdigest, TPM_NONCE_SIZE, enonce1, + TPM_NONCE_SIZE, ononce, 1, continueflag1, 0, 0); + if (ret < 0) + goto out; + if (memcmp(testhmac1, authdata1, SHA1_DIGEST_SIZE)) { + ret = -EINVAL; + goto out; + } + ret = TSS_rawhmac(testhmac2, key2, keylen2, SHA1_DIGEST_SIZE, + paramdigest, TPM_NONCE_SIZE, enonce2, + TPM_NONCE_SIZE, ononce, 1, continueflag2, 0, 0); + if (ret < 0) + goto out; + if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE)) + ret = -EINVAL; +out: + kfree(sdesc); + return ret; +} + +/* + * For key specific tpm requests, we will generate and send our + * own TPM command packets using the drivers send function. + */ +static int trusted_tpm_send(struct tpm_chip *chip, unsigned char *cmd, + size_t buflen, const char *desc) +{ + int rc; + + dump_tpm_buf(cmd); + rc = tpm_send_command(chip, cmd, buflen, desc); + dump_tpm_buf(cmd); + if (rc > 0) + /* Can't return positive return codes values to keyctl */ + rc = -EPERM; + return rc; +} + +/* + * Create an object specific authorisation protocol (OSAP) session + */ +static int osap(struct tpm_chip *chip, + struct tpm_buf *tb, struct osapsess *s, + const unsigned char *key, uint16_t type, uint32_t handle) +{ + unsigned char enonce[TPM_NONCE_SIZE]; + unsigned char ononce[TPM_NONCE_SIZE]; + int ret; + + ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE); + if (ret != TPM_NONCE_SIZE) + return ret; + + INIT_BUF(tb); + store16(tb, TPM_TAG_RQU_COMMAND); + store32(tb, TPM_OSAP_SIZE); + store32(tb, TPM_ORD_OSAP); + store16(tb, type); + store32(tb, handle); + storebytes(tb, ononce, TPM_NONCE_SIZE); + + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, + "creating OSAP session"); + if (ret < 0) + return ret; + + s->handle = LOAD32(tb->data, TPM_DATA_OFFSET); + memcpy(s->enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)]), + TPM_NONCE_SIZE); + memcpy(enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) + + TPM_NONCE_SIZE]), TPM_NONCE_SIZE); + return TSS_rawhmac(s->secret, key, SHA1_DIGEST_SIZE, TPM_NONCE_SIZE, + enonce, TPM_NONCE_SIZE, ononce, 0, 0); +} + +/* + * Create an object independent authorisation protocol (oiap) session + */ +static int oiap(struct tpm_chip *chip, struct tpm_buf *tb, uint32_t *handle, + unsigned char *nonce) +{ + int ret; + + INIT_BUF(tb); + store16(tb, TPM_TAG_RQU_COMMAND); + store32(tb, TPM_OIAP_SIZE); + store32(tb, TPM_ORD_OIAP); + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, + "creating OIAP session"); + if (ret < 0) + return ret; + + *handle = LOAD32(tb->data, TPM_DATA_OFFSET); + memcpy(nonce, &tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)], + TPM_NONCE_SIZE); + return 0; +} + +struct tpm_digests { + unsigned char encauth[SHA1_DIGEST_SIZE]; + unsigned char pubauth[SHA1_DIGEST_SIZE]; + unsigned char xorwork[SHA1_DIGEST_SIZE * 2]; + unsigned char xorhash[SHA1_DIGEST_SIZE]; + unsigned char nonceodd[TPM_NONCE_SIZE]; +}; + +/* + * Have the TPM seal(encrypt) the trusted key, possibly based on + * Platform Configuration Registers (PCRs). AUTH1 for sealing key. + */ +int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, + uint32_t keyhandle, const unsigned char *keyauth, + const unsigned char *data, uint32_t datalen, + unsigned char *blob, uint32_t *bloblen, + const unsigned char *blobauth, + const unsigned char *pcrinfo, uint32_t pcrinfosize) +{ + struct osapsess sess; + struct tpm_digests *td; + unsigned char cont; + uint32_t ordinal; + uint32_t pcrsize; + uint32_t datsize; + int sealinfosize; + int encdatasize; + int storedsize; + int ret; + int i; + + /* alloc some work space for all the hashes */ + td = kmalloc(sizeof *td, GFP_KERNEL); + if (!td) + return -ENOMEM; + + /* get session for sealing key */ + ret = osap(chip, tb, &sess, keyauth, keytype, keyhandle); + if (ret < 0) + goto out; + dump_sess(&sess); + + /* calculate encrypted authorization value */ + memcpy(td->xorwork, sess.secret, SHA1_DIGEST_SIZE); + memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE); + ret = TSS_sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash); + if (ret < 0) + goto out; + + ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE); + if (ret != TPM_NONCE_SIZE) + goto out; + ordinal = htonl(TPM_ORD_SEAL); + datsize = htonl(datalen); + pcrsize = htonl(pcrinfosize); + cont = 0; + + /* encrypt data authorization key */ + for (i = 0; i < SHA1_DIGEST_SIZE; ++i) + td->encauth[i] = td->xorhash[i] ^ blobauth[i]; + + /* calculate authorization HMAC value */ + if (pcrinfosize == 0) { + /* no pcr info specified */ + ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, + sess.enonce, td->nonceodd, cont, + sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, + td->encauth, sizeof(uint32_t), &pcrsize, + sizeof(uint32_t), &datsize, datalen, data, 0, + 0); + } else { + /* pcr info specified */ + ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, + sess.enonce, td->nonceodd, cont, + sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, + td->encauth, sizeof(uint32_t), &pcrsize, + pcrinfosize, pcrinfo, sizeof(uint32_t), + &datsize, datalen, data, 0, 0); + } + if (ret < 0) + goto out; + + /* build and send the TPM request packet */ + INIT_BUF(tb); + store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); + store32(tb, TPM_SEAL_SIZE + pcrinfosize + datalen); + store32(tb, TPM_ORD_SEAL); + store32(tb, keyhandle); + storebytes(tb, td->encauth, SHA1_DIGEST_SIZE); + store32(tb, pcrinfosize); + storebytes(tb, pcrinfo, pcrinfosize); + store32(tb, datalen); + storebytes(tb, data, datalen); + store32(tb, sess.handle); + storebytes(tb, td->nonceodd, TPM_NONCE_SIZE); + store8(tb, cont); + storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE); + + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, + "sealing data"); + if (ret < 0) + goto out; + + /* calculate the size of the returned Blob */ + sealinfosize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t)); + encdatasize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t) + + sizeof(uint32_t) + sealinfosize); + storedsize = sizeof(uint32_t) + sizeof(uint32_t) + sealinfosize + + sizeof(uint32_t) + encdatasize; + + /* check the HMAC in the response */ + ret = TSS_checkhmac1(tb->data, ordinal, td->nonceodd, sess.secret, + SHA1_DIGEST_SIZE, storedsize, TPM_DATA_OFFSET, 0, + 0); + + /* copy the returned blob to caller */ + if (!ret) { + memcpy(blob, tb->data + TPM_DATA_OFFSET, storedsize); + *bloblen = storedsize; + } +out: + kfree(td); + return ret; +} +EXPORT_SYMBOL_GPL(tpm_seal); + +/* + * use the AUTH2_COMMAND form of unseal, to authorize both key and blob + */ +int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, + uint32_t keyhandle, const unsigned char *keyauth, + const unsigned char *blob, int bloblen, + const unsigned char *blobauth, + unsigned char *data, unsigned int *datalen) +{ + unsigned char nonceodd[TPM_NONCE_SIZE]; + unsigned char enonce1[TPM_NONCE_SIZE]; + unsigned char enonce2[TPM_NONCE_SIZE]; + unsigned char authdata1[SHA1_DIGEST_SIZE]; + unsigned char authdata2[SHA1_DIGEST_SIZE]; + uint32_t authhandle1 = 0; + uint32_t authhandle2 = 0; + unsigned char cont = 0; + uint32_t ordinal; + uint32_t keyhndl; + int ret; + + /* sessions for unsealing key and data */ + ret = oiap(chip, tb, &authhandle1, enonce1); + if (ret < 0) { + pr_info("trusted_key: oiap failed (%d)\n", ret); + return ret; + } + ret = oiap(chip, tb, &authhandle2, enonce2); + if (ret < 0) { + pr_info("trusted_key: oiap failed (%d)\n", ret); + return ret; + } + + ordinal = htonl(TPM_ORD_UNSEAL); + keyhndl = htonl(SRKHANDLE); + ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE); + if (ret != TPM_NONCE_SIZE) { + pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); + return ret; + } + ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, + enonce1, nonceodd, cont, sizeof(uint32_t), + &ordinal, bloblen, blob, 0, 0); + if (ret < 0) + return ret; + ret = TSS_authhmac(authdata2, blobauth, TPM_NONCE_SIZE, + enonce2, nonceodd, cont, sizeof(uint32_t), + &ordinal, bloblen, blob, 0, 0); + if (ret < 0) + return ret; + + /* build and send TPM request packet */ + INIT_BUF(tb); + store16(tb, TPM_TAG_RQU_AUTH2_COMMAND); + store32(tb, TPM_UNSEAL_SIZE + bloblen); + store32(tb, TPM_ORD_UNSEAL); + store32(tb, keyhandle); + storebytes(tb, blob, bloblen); + store32(tb, authhandle1); + storebytes(tb, nonceodd, TPM_NONCE_SIZE); + store8(tb, cont); + storebytes(tb, authdata1, SHA1_DIGEST_SIZE); + store32(tb, authhandle2); + storebytes(tb, nonceodd, TPM_NONCE_SIZE); + store8(tb, cont); + storebytes(tb, authdata2, SHA1_DIGEST_SIZE); + + ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, + "unsealing data"); + if (ret < 0) { + pr_info("trusted_key: authhmac failed (%d)\n", ret); + return ret; + } + + *datalen = LOAD32(tb->data, TPM_DATA_OFFSET); + ret = TSS_checkhmac2(tb->data, ordinal, nonceodd, + keyauth, SHA1_DIGEST_SIZE, + blobauth, SHA1_DIGEST_SIZE, + sizeof(uint32_t), TPM_DATA_OFFSET, + *datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0, + 0); + if (ret < 0) { + pr_info("trusted_key: TSS_checkhmac2 failed (%d)\n", ret); + return ret; + } + memcpy(data, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *datalen); + return 0; +} +EXPORT_SYMBOL_GPL(tpm_unseal); + +void trusted_shash_release(void) +{ + if (hashalg) + crypto_free_shash(hashalg); + if (hmacalg) + crypto_free_shash(hmacalg); +} +EXPORT_SYMBOL_GPL(trusted_shash_release); + +int trusted_shash_alloc(void) +{ + int ret; + + hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(hmacalg)) { + pr_info("trusted_key: could not allocate crypto %s\n", + hmac_alg); + return PTR_ERR(hmacalg); + } + + hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(hashalg)) { + pr_info("trusted_key: could not allocate crypto %s\n", + hash_alg); + ret = PTR_ERR(hashalg); + goto hashalg_fail; + } + + return 0; + +hashalg_fail: + crypto_free_shash(hmacalg); + return ret; +} +EXPORT_SYMBOL_GPL(trusted_shash_alloc); diff --git a/drivers/char/tpm/tpm-library.h b/drivers/char/tpm/tpm-library.h new file mode 100644 index 000000000000..eec1dfe26c2a --- /dev/null +++ b/drivers/char/tpm/tpm-library.h @@ -0,0 +1,83 @@ +/* TPM call wrapper library internal definitions. + * + * Copyright (C) 2010 IBM Corporation + * + * Author: + * David Safford <sa...@us...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + */ + + +#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset])) +#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset]) +#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset])) + +struct osapsess { + uint32_t handle; + unsigned char secret[SHA1_DIGEST_SIZE]; + unsigned char enonce[TPM_NONCE_SIZE]; +}; + +static inline void store8(struct tpm_buf *buf, const unsigned char value) +{ + buf->data[buf->len++] = value; +} + +static inline void store16(struct tpm_buf *buf, const uint16_t value) +{ + *(uint16_t *) & buf->data[buf->len] = htons(value); + buf->len += sizeof value; +} + +static inline void store32(struct tpm_buf *buf, const uint32_t value) +{ + *(uint32_t *) & buf->data[buf->len] = htonl(value); + buf->len += sizeof value; +} + +static inline void storebytes(struct tpm_buf *buf, const unsigned char *in, + const int len) +{ + memcpy(buf->data + buf->len, in, len); + buf->len += len; +} + +/* + * Debugging + */ +#define TPM_DEBUG 0 + +#ifdef TPM_DEBUG +static inline void dump_sess(struct osapsess *s) +{ + print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE, + 16, 1, &s->handle, 4, 0); + pr_info("trusted-key: secret:\n"); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, + 16, 1, &s->secret, SHA1_DIGEST_SIZE, 0); + pr_info("trusted-key: enonce:\n"); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, + 16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0); +} + +static inline void dump_tpm_buf(unsigned char *buf) +{ + int len; + + pr_info("\ntrusted-key: tpm buffer\n"); + len = LOAD32(buf, TPM_SIZE_OFFSET); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0); +} + +#else +static inline void dump_sess(struct osapsess *s) +{ +} + +static inline void dump_tpm_buf(unsigned char *buf) +{ +} +#endif diff --git a/include/linux/tpm.h b/include/linux/tpm.h index f4e14405f5cf..5d8caf56c272 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -77,4 +77,42 @@ static inline int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max) { return -ENODEV; } #endif + +/* + * TPM call library. + */ +/* implementation specific TPM constants */ +#define MAX_PCRINFO_SIZE 64 +#define MAX_BUF_SIZE 512 +#define TPM_GETRANDOM_SIZE 14 +#define TPM_OSAP_SIZE 36 +#define TPM_OIAP_SIZE 10 +#define TPM_SEAL_SIZE 87 +#define TPM_UNSEAL_SIZE 104 +#define TPM_SIZE_OFFSET 2 +#define TPM_RETURN_OFFSET 6 +#define TPM_DATA_OFFSET 10 + +struct tpm_buf { + int len; + unsigned char data[MAX_BUF_SIZE]; +}; + +#define INIT_BUF(tb) (tb->len = 0) + +extern void trusted_shash_release(void); +extern int trusted_shash_alloc(void); + +extern int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, + uint32_t keyhandle, const unsigned char *keyauth, + const unsigned char *data, uint32_t datalen, + unsigned char *blob, uint32_t *bloblen, + const unsigned char *blobauth, + const unsigned char *pcrinfo, uint32_t pcrinfosize); +extern int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, + uint32_t keyhandle, const unsigned char *keyauth, + const unsigned char *blob, int bloblen, + const unsigned char *blobauth, + unsigned char *data, unsigned int *datalen); + #endif diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 943c65b53201..83c6a485e62a 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -31,343 +31,6 @@ #include "trusted.h" -static const char hmac_alg[] = "hmac(sha1)"; -static const char hash_alg[] = "sha1"; - -struct sdesc { - struct shash_desc shash; - char ctx[]; -}; - -static struct crypto_shash *hashalg; -static struct crypto_shash *hmacalg; - -static struct sdesc *init_sdesc(struct crypto_shash *alg) -{ - struct sdesc *sdesc; - int size; - - size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); - sdesc = kmalloc(size, GFP_KERNEL); - if (!sdesc) - return ERR_PTR(-ENOMEM); - sdesc->shash.tfm = alg; - sdesc->shash.flags = 0x0; - return sdesc; -} - -static int TSS_sha1(const unsigned char *data, unsigned int datalen, - unsigned char *digest) -{ - struct sdesc *sdesc; - int ret; - - sdesc = init_sdesc(hashalg); - if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); - return PTR_ERR(sdesc); - } - - ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); - kfree(sdesc); - return ret; -} - -static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, - unsigned int keylen, ...) -{ - struct sdesc *sdesc; - va_list argp; - unsigned int dlen; - unsigned char *data; - int ret; - - sdesc = init_sdesc(hmacalg); - if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hmac_alg); - return PTR_ERR(sdesc); - } - - ret = crypto_shash_setkey(hmacalg, key, keylen); - if (ret < 0) - goto out; - ret = crypto_shash_init(&sdesc->shash); - if (ret < 0) - goto out; - - va_start(argp, keylen); - for (;;) { - dlen = va_arg(argp, unsigned int); - if (dlen == 0) - break; - data = va_arg(argp, unsigned char *); - if (data == NULL) { - ret = -EINVAL; - break; - } - ret = crypto_shash_update(&sdesc->shash, data, dlen); - if (ret < 0) - break; - } - va_end(argp); - if (!ret) - ret = crypto_shash_final(&sdesc->shash, digest); -out: - kfree(sdesc); - return ret; -} - -/* - * calculate authorization info fields to send to TPM - */ -static int TSS_authhmac(unsigned char *digest, const unsigned char *key, - unsigned int keylen, unsigned char *h1, - unsigned char *h2, unsigned char h3, ...) -{ - unsigned char paramdigest[SHA1_DIGEST_SIZE]; - struct sdesc *sdesc; - unsigned int dlen; - unsigned char *data; - unsigned char c; - int ret; - va_list argp; - - sdesc = init_sdesc(hashalg); - if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); - return PTR_ERR(sdesc); - } - - c = h3; - ret = crypto_shash_init(&sdesc->shash); - if (ret < 0) - goto out; - va_start(argp, h3); - for (;;) { - dlen = va_arg(argp, unsigned int); - if (dlen == 0) - break; - data = va_arg(argp, unsigned char *); - if (!data) { - ret = -EINVAL; - break; - } - ret = crypto_shash_update(&sdesc->shash, data, dlen); - if (ret < 0) - break; - } - va_end(argp); - if (!ret) - ret = crypto_shash_final(&sdesc->shash, paramdigest); - if (!ret) - ret = TSS_rawhmac(digest, key, keylen, SHA1_DIGEST_SIZE, - paramdigest, TPM_NONCE_SIZE, h1, - TPM_NONCE_SIZE, h2, 1, &c, 0, 0); -out: - kfree(sdesc); - return ret; -} - -/* - * verify the AUTH1_COMMAND (Seal) result from TPM - */ -static int TSS_checkhmac1(unsigned char *buffer, - const uint32_t command, - const unsigned char *ononce, - const unsigned char *key, - unsigned int keylen, ...) -{ - uint32_t bufsize; - uint16_t tag; - uint32_t ordinal; - uint32_t result; - unsigned char *enonce; - unsigned char *continueflag; - unsigned char *authdata; - unsigned char testhmac[SHA1_DIGEST_SIZE]; - unsigned char paramdigest[SHA1_DIGEST_SIZE]; - struct sdesc *sdesc; - unsigned int dlen; - unsigned int dpos; - va_list argp; - int ret; - - bufsize = LOAD32(buffer, TPM_SIZE_OFFSET); - tag = LOAD16(buffer, 0); - ordinal = command; - result = LOAD32N(buffer, TPM_RETURN_OFFSET); - if (tag == TPM_TAG_RSP_COMMAND) - return 0; - if (tag != TPM_TAG_RSP_AUTH1_COMMAND) - return -EINVAL; - authdata = buffer + bufsize - SHA1_DIGEST_SIZE; - continueflag = authdata - 1; - enonce = continueflag - TPM_NONCE_SIZE; - - sdesc = init_sdesc(hashalg); - if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); - return PTR_ERR(sdesc); - } - ret = crypto_shash_init(&sdesc->shash); - if (ret < 0) - goto out; - ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, - sizeof result); - if (ret < 0) - goto out; - ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, - sizeof ordinal); - if (ret < 0) - goto out; - va_start(argp, keylen); - for (;;) { - dlen = va_arg(argp, unsigned int); - if (dlen == 0) - break; - dpos = va_arg(argp, unsigned int); - ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); - if (ret < 0) - break; - } - va_end(argp); - if (!ret) - ret = crypto_shash_final(&sdesc->shash, paramdigest); - if (ret < 0) - goto out; - - ret = TSS_rawhmac(testhmac, key, keylen, SHA1_DIGEST_SIZE, paramdigest, - TPM_NONCE_SIZE, enonce, TPM_NONCE_SIZE, ononce, - 1, continueflag, 0, 0); - if (ret < 0) - goto out; - - if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE)) - ret = -EINVAL; -out: - kfree(sdesc); - return ret; -} - -/* - * verify the AUTH2_COMMAND (unseal) result from TPM - */ -static int TSS_checkhmac2(unsigned char *buffer, - const uint32_t command, - const unsigned char *ononce, - const unsigned char *key1, - unsigned int keylen1, - const unsigned char *key2, - unsigned int keylen2, ...) -{ - uint32_t bufsize; - uint16_t tag; - uint32_t ordinal; - uint32_t result; - unsigned char *enonce1; - unsigned char *continueflag1; - unsigned char *authdata1; - unsigned char *enonce2; - unsigned char *continueflag2; - unsigned char *authdata2; - unsigned char testhmac1[SHA1_DIGEST_SIZE]; - unsigned char testhmac2[SHA1_DIGEST_SIZE]; - unsigned char paramdigest[SHA1_DIGEST_SIZE]; - struct sdesc *sdesc; - unsigned int dlen; - unsigned int dpos; - va_list argp; - int ret; - - bufsize = LOAD32(buffer, TPM_SIZE_OFFSET); - tag = LOAD16(buffer, 0); - ordinal = command; - result = LOAD32N(buffer, TPM_RETURN_OFFSET); - - if (tag == TPM_TAG_RSP_COMMAND) - return 0; - if (tag != TPM_TAG_RSP_AUTH2_COMMAND) - return -EINVAL; - authdata1 = buffer + bufsize - (SHA1_DIGEST_SIZE + 1 - + SHA1_DIGEST_SIZE + SHA1_DIGEST_SIZE); - authdata2 = buffer + bufsize - (SHA1_DIGEST_SIZE); - continueflag1 = authdata1 - 1; - continueflag2 = authdata2 - 1; - enonce1 = continueflag1 - TPM_NONCE_SIZE; - enonce2 = continueflag2 - TPM_NONCE_SIZE; - - sdesc = init_sdesc(hashalg); - if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); - return PTR_ERR(sdesc); - } - ret = crypto_shash_init(&sdesc->shash); - if (ret < 0) - goto out; - ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, - sizeof result); - if (ret < 0) - goto out; - ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, - sizeof ordinal); - if (ret < 0) - goto out; - - va_start(argp, keylen2); - for (;;) { - dlen = va_arg(argp, unsigned int); - if (dlen == 0) - break; - dpos = va_arg(argp, unsigned int); - ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); - if (ret < 0) - break; - } - va_end(argp); - if (!ret) - ret = crypto_shash_final(&sdesc->shash, paramdigest); - if (ret < 0) - goto out; - - ret = TSS_rawhmac(testhmac1, key1, keylen1, SHA1_DIGEST_SIZE, - paramdigest, TPM_NONCE_SIZE, enonce1, - TPM_NONCE_SIZE, ononce, 1, continueflag1, 0, 0); - if (ret < 0) - goto out; - if (memcmp(testhmac1, authdata1, SHA1_DIGEST_SIZE)) { - ret = -EINVAL; - goto out; - } - ret = TSS_rawhmac(testhmac2, key2, keylen2, SHA1_DIGEST_SIZE, - paramdigest, TPM_NONCE_SIZE, enonce2, - TPM_NONCE_SIZE, ononce, 1, continueflag2, 0, 0); - if (ret < 0) - goto out; - if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE)) - ret = -EINVAL; -out: - kfree(sdesc); - return ret; -} - -/* - * For key specific tpm requests, we will generate and send our - * own TPM command packets using the drivers send function. - */ -static int trusted_tpm_send(struct tpm_chip *chip, unsigned char *cmd, - size_t buflen, const char *desc) -{ - int rc; - - dump_tpm_buf(cmd); - rc = tpm_send_command(chip, cmd, buflen, desc); - dump_tpm_buf(cmd); - if (rc > 0) - /* Can't return positive return codes values to keyctl */ - rc = -EPERM; - return rc; -} - /* * Lock a trusted key, by extending a selected PCR. * @@ -387,281 +50,6 @@ static int pcrlock(struct tpm_chip *chip, const int pcrnum) return tpm_pcr_extend(chip, pcrnum, hash) ? -EINVAL : 0; } -/* - * Create an object specific authorisation protocol (OSAP) session - */ -static int osap(struct tpm_chip *chip, - struct tpm_buf *tb, struct osapsess *s, - const unsigned char *key, uint16_t type, uint32_t handle) -{ - unsigned char enonce[TPM_NONCE_SIZE]; - unsigned char ononce[TPM_NONCE_SIZE]; - int ret; - - ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE); - if (ret != TPM_NONCE_SIZE) - return ret; - - INIT_BUF(tb); - store16(tb, TPM_TAG_RQU_COMMAND); - store32(tb, TPM_OSAP_SIZE); - store32(tb, TPM_ORD_OSAP); - store16(tb, type); - store32(tb, handle); - storebytes(tb, ononce, TPM_NONCE_SIZE); - - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, - "creating OSAP session"); - if (ret < 0) - return ret; - - s->handle = LOAD32(tb->data, TPM_DATA_OFFSET); - memcpy(s->enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)]), - TPM_NONCE_SIZE); - memcpy(enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) + - TPM_NONCE_SIZE]), TPM_NONCE_SIZE); - return TSS_rawhmac(s->secret, key, SHA1_DIGEST_SIZE, TPM_NONCE_SIZE, - enonce, TPM_NONCE_SIZE, ononce, 0, 0); -} - -/* - * Create an object independent authorisation protocol (oiap) session - */ -static int oiap(struct tpm_chip *chip, struct tpm_buf *tb, uint32_t *handle, - unsigned char *nonce) -{ - int ret; - - INIT_BUF(tb); - store16(tb, TPM_TAG_RQU_COMMAND); - store32(tb, TPM_OIAP_SIZE); - store32(tb, TPM_ORD_OIAP); - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, - "creating OIAP session"); - if (ret < 0) - return ret; - - *handle = LOAD32(tb->data, TPM_DATA_OFFSET); - memcpy(nonce, &tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)], - TPM_NONCE_SIZE); - return 0; -} - -struct tpm_digests { - unsigned char encauth[SHA1_DIGEST_SIZE]; - unsigned char pubauth[SHA1_DIGEST_SIZE]; - unsigned char xorwork[SHA1_DIGEST_SIZE * 2]; - unsigned char xorhash[SHA1_DIGEST_SIZE]; - unsigned char nonceodd[TPM_NONCE_SIZE]; -}; - -/* - * Have the TPM seal(encrypt) the trusted key, possibly based on - * Platform Configuration Registers (PCRs). AUTH1 for sealing key. - */ -static int tpm_seal(struct tpm_chip *chip, - struct tpm_buf *tb, uint16_t keytype, - uint32_t keyhandle, const unsigned char *keyauth, - const unsigned char *data, uint32_t datalen, - unsigned char *blob, uint32_t *bloblen, - const unsigned char *blobauth, - const unsigned char *pcrinfo, uint32_t pcrinfosize) -{ - struct osapsess sess; - struct tpm_digests *td; - unsigned char cont; - uint32_t ordinal; - uint32_t pcrsize; - uint32_t datsize; - int sealinfosize; - int encdatasize; - int storedsize; - int ret; - int i; - - /* alloc some work space for all the hashes */ - td = kmalloc(sizeof *td, GFP_KERNEL); - if (!td) - return -ENOMEM; - - /* get session for sealing key */ - ret = osap(chip, tb, &sess, keyauth, keytype, keyhandle); - if (ret < 0) - goto out; - dump_sess(&sess); - - /* calculate encrypted authorization value */ - memcpy(td->xorwork, sess.secret, SHA1_DIGEST_SIZE); - memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE); - ret = TSS_sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash); - if (ret < 0) - goto out; - - ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE); - if (ret != TPM_NONCE_SIZE) - goto out; - ordinal = htonl(TPM_ORD_SEAL); - datsize = htonl(datalen); - pcrsize = htonl(pcrinfosize); - cont = 0; - - /* encrypt data authorization key */ - for (i = 0; i < SHA1_DIGEST_SIZE; ++i) - td->encauth[i] = td->xorhash[i] ^ blobauth[i]; - - /* calculate authorization HMAC value */ - if (pcrinfosize == 0) { - /* no pcr info specified */ - ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, - sess.enonce, td->nonceodd, cont, - sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, - td->encauth, sizeof(uint32_t), &pcrsize, - sizeof(uint32_t), &datsize, datalen, data, 0, - 0); - } else { - /* pcr info specified */ - ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, - sess.enonce, td->nonceodd, cont, - sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, - td->encauth, sizeof(uint32_t), &pcrsize, - pcrinfosize, pcrinfo, sizeof(uint32_t), - &datsize, datalen, data, 0, 0); - } - if (ret < 0) - goto out; - - /* build and send the TPM request packet */ - INIT_BUF(tb); - store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); - store32(tb, TPM_SEAL_SIZE + pcrinfosize + datalen); - store32(tb, TPM_ORD_SEAL); - store32(tb, keyhandle); - storebytes(tb, td->encauth, SHA1_DIGEST_SIZE); - store32(tb, pcrinfosize); - storebytes(tb, pcrinfo, pcrinfosize); - store32(tb, datalen); - storebytes(tb, data, datalen); - store32(tb, sess.handle); - storebytes(tb, td->nonceodd, TPM_NONCE_SIZE); - store8(tb, cont); - storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE); - - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, - "sealing data"); - if (ret < 0) - goto out; - - /* calculate the size of the returned Blob */ - sealinfosize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t)); - encdatasize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t) + - sizeof(uint32_t) + sealinfosize); - storedsize = sizeof(uint32_t) + sizeof(uint32_t) + sealinfosize + - sizeof(uint32_t) + encdatasize; - - /* check the HMAC in the response */ - ret = TSS_checkhmac1(tb->data, ordinal, td->nonceodd, sess.secret, - SHA1_DIGEST_SIZE, storedsize, TPM_DATA_OFFSET, 0, - 0); - - /* copy the returned blob to caller */ - if (!ret) { - memcpy(blob, tb->data + TPM_DATA_OFFSET, storedsize); - *bloblen = storedsize; - } -out: - kfree(td); - return ret; -} - -/* - * use the AUTH2_COMMAND form of unseal, to authorize both key and blob - */ -static int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, - uint32_t keyhandle, const unsigned char *keyauth, - const unsigned char *blob, int bloblen, - const unsigned char *blobauth, - unsigned char *data, unsigned int *datalen) -{ - unsigned char nonceodd[TPM_NONCE_SIZE]; - unsigned char enonce1[TPM_NONCE_SIZE]; - unsigned char enonce2[TPM_NONCE_SIZE]; - unsigned char authdata1[SHA1_DIGEST_SIZE]; - unsigned char authdata2[SHA1_DIGEST_SIZE]; - uint32_t authhandle1 = 0; - uint32_t authhandle2 = 0; - unsigned char cont = 0; - uint32_t ordinal; - uint32_t keyhndl; - int ret; - - /* sessions for unsealing key and data */ - ret = oiap(chip, tb, &authhandle1, enonce1); - if (ret < 0) { - pr_info("trusted_key: oiap failed (%d)\n", ret); - return ret; - } - ret = oiap(chip, tb, &authhandle2, enonce2); - if (ret < 0) { - pr_info("trusted_key: oiap failed (%d)\n", ret); - return ret; - } - - ordinal = htonl(TPM_ORD_UNSEAL); - keyhndl = htonl(SRKHANDLE); - ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE); - if (ret != TPM_NONCE_SIZE) { - pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); - return ret; - } - ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, - enonce1, nonceodd, cont, sizeof(uint32_t), - &ordinal, bloblen, blob, 0, 0); - if (ret < 0) - return ret; - ret = TSS_authhmac(authdata2, blobauth, TPM_NONCE_SIZE, - enonce2, nonceodd, cont, sizeof(uint32_t), - &ordinal, bloblen, blob, 0, 0); - if (ret < 0) - return ret; - - /* build and send TPM request packet */ - INIT_BUF(tb); - store16(tb, TPM_TAG_RQU_AUTH2_COMMAND); - store32(tb, TPM_UNSEAL_SIZE + bloblen); - store32(tb, TPM_ORD_UNSEAL); - store32(tb, keyhandle); - storebytes(tb, blob, bloblen); - store32(tb, authhandle1); - storebytes(tb, nonceodd, TPM_NONCE_SIZE); - store8(tb, cont); - storebytes(tb, authdata1, SHA1_DIGEST_SIZE); - store32(tb, authhandle2); - storebytes(tb, nonceodd, TPM_NONCE_SIZE); - store8(tb, cont); - storebytes(tb, authdata2, SHA1_DIGEST_SIZE); - - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, - "unsealing data"); - if (ret < 0) { - pr_info("trusted_key: authhmac failed (%d)\n", ret); - return ret; - } - - *datalen = LOAD32(tb->data, TPM_DATA_OFFSET); - ret = TSS_checkhmac2(tb->data, ordinal, nonceodd, - keyauth, SHA1_DIGEST_SIZE, - blobauth, SHA1_DIGEST_SIZE, - sizeof(uint32_t), TPM_DATA_OFFSET, - *datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0, - 0); - if (ret < 0) { - pr_info("trusted_key: TSS_checkhmac2 failed (%d)\n", ret); - return ret; - } - memcpy(data, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *datalen); - return 0; -} - /* * Have the TPM seal(encrypt) the symmetric key */ @@ -1129,40 +517,6 @@ struct key_type key_type_trusted = { EXPORT_SYMBOL_GPL(key_type_trusted); -static void trusted_shash_release(void) -{ - if (hashalg) - crypto_free_shash(hashalg); - if (hmacalg) - crypto_free_shash(hmacalg); -} - -static int __init trusted_shash_alloc(void) -{ - int ret; - - hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(hmacalg)) { - pr_info("trusted_key: could not allocate crypto %s\n", - hmac_alg); - return PTR_ERR(hmacalg); - } - - hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(hashalg)) { - pr_info("trusted_key: could not allocate crypto %s\n", - hash_alg); - ret = PTR_ERR(hashalg); - goto hashalg_fail; - } - - return 0; - -hashalg_fail: - crypto_free_shash(hmacalg); - return ret; -} - static int __init init_trusted(void) { int ret; diff --git a/security/keys/trusted.h b/security/keys/trusted.h index 3249fbd2b653..61fe24d5c6b3 100644 --- a/security/keys/trusted.h +++ b/security/keys/trusted.h @@ -1,35 +1,6 @@ #ifndef __TRUSTED_KEY_H #define __TRUSTED_KEY_H -/* implementation specific TPM constants */ -#define MAX_PCRINFO_SIZE 64 -#define MAX_BUF_SIZE 512 -#define TPM_GETRANDOM_SIZE 14 -#define TPM_OSAP_SIZE 36 -#define TPM_OIAP_SIZE 10 -#define TPM_SEAL_SIZE 87 -#define TPM_UNSEAL_SIZE 104 -#define TPM_SIZE_OFFSET 2 -#define TPM_RETURN_OFFSET 6 -#define TPM_DATA_OFFSET 10 - -#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset])) -#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset]) -#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset])) - -struct tpm_buf { - int len; - unsigned char data[MAX_BUF_SIZE]; -}; - -#define INIT_BUF(tb) (tb->len = 0) - -struct osapsess { - uint32_t handle; - unsigned char secret[SHA1_DIGEST_SIZE]; - unsigned char enonce[TPM_NONCE_SIZE]; -}; - /* discrete values, but have to store in uint16_t for TPM use */ enum { SEAL_keytype = 1, @@ -70,26 +41,6 @@ static inline void dump_payload(struct trusted_key_payload *p) pr_info("trusted_key: migratable %d\n", p->migratable); } -static inline void dump_sess(struct osapsess *s) -{ - print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE, - 16, 1, &s->handle, 4, 0); - pr_info("trusted-key: secret:\n"); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, - 16, 1, &s->secret, SHA1_DIGEST_SIZE, 0); - pr_info("trusted-key: enonce:\n"); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, - 16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0); -} - -static inline void dump_tpm_buf(unsigned char *buf) -{ - int len; - - pr_info("\ntrusted-key: tpm buffer\n"); - len = LOAD32(buf, TPM_SIZE_OFFSET); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0); -} #else static inline void dump_options(struct trusted_key_options *o) { @@ -99,36 +50,5 @@ static inline void dump_payload(struct trusted_key_payload *p) { } -static inline void dump_sess(struct osapsess *s) -{ -} - -static inline void dump_tpm_buf(unsigned char *buf) -{ -} #endif - -static inline void store8(struct tpm_buf *buf, const unsigned char value) -{ - buf->data[buf->len++] = value; -} - -static inline void store16(struct tpm_buf *buf, const uint16_t value) -{ - *(uint16_t *) & buf->data[buf->len] = htons(value); - buf->len += sizeof value; -} - -static inline void store32(struct tpm_buf *buf, const uint32_t value) -{ - *(uint32_t *) & buf->data[buf->len] = htonl(value); - buf->len += sizeof value; -} - -static inline void storebytes(struct tpm_buf *buf, const unsigned char *in, - const int len) -{ - memcpy(buf->data + buf->len, in, len); - buf->len += len; -} #endif |
From: David H. <dho...@re...> - 2018-08-21 15:57:59
|
Clean up the TPM library file that was split out of trusted keys. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-library.c | 187 +++++++++++++++++++++++----------------- drivers/char/tpm/tpm-library.h | 14 +-- include/linux/tpm.h | 4 - security/keys/trusted.c | 6 + 4 files changed, 121 insertions(+), 90 deletions(-) diff --git a/drivers/char/tpm/tpm-library.c b/drivers/char/tpm/tpm-library.c index 1be4f71cabcb..e4cfc1f090e1 100644 --- a/drivers/char/tpm/tpm-library.c +++ b/drivers/char/tpm/tpm-library.c @@ -8,44 +8,36 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2 of the License. - * - * See Documentation/security/keys-trusted-encrypted.txt */ -#include <linux/uaccess.h> -#include <linux/module.h> -#include <linux/init.h> +#define pr_fmt(fmt) "TPMLIB: "fmt #include <linux/slab.h> -#include <linux/parser.h> -#include <linux/string.h> #include <linux/err.h> -#include <keys/user-type.h> -#include <keys/trusted-type.h> -#include <linux/key-type.h> -#include <linux/rcupdate.h> +#include <linux/mutex.h> #include <linux/crypto.h> #include <crypto/hash.h> #include <crypto/sha.h> -#include <linux/capability.h> #include <linux/tpm.h> #include <linux/tpm_command.h> #include "tpm-library.h" -static const char hmac_alg[] = "hmac(sha1)"; -static const char hash_alg[] = "sha1"; +static const char tpm_hmac_alg[] = "hmac(sha1)"; +static const char tpm_hash_alg[] = "sha1"; -struct sdesc { +struct tpm_sdesc { struct shash_desc shash; char ctx[]; }; -static struct crypto_shash *hashalg; -static struct crypto_shash *hmacalg; +static DEFINE_MUTEX(tpm_library_init_mutex); +static atomic_t tpm_library_usage; +static struct crypto_shash *tpm_hashalg; +static struct crypto_shash *tpm_hmacalg; -static struct sdesc *init_sdesc(struct crypto_shash *alg) +static struct tpm_sdesc *tpm_init_sdesc(struct crypto_shash *alg) { - struct sdesc *sdesc; + struct tpm_sdesc *sdesc; int size; size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); @@ -60,12 +52,12 @@ static struct sdesc *init_sdesc(struct crypto_shash *alg) static int TSS_sha1(const unsigned char *data, unsigned int datalen, unsigned char *digest) { - struct sdesc *sdesc; + struct tpm_sdesc *sdesc; int ret; - sdesc = init_sdesc(hashalg); + sdesc = tpm_init_sdesc(tpm_hashalg); if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); + pr_info("Can't alloc %s\n", tpm_hash_alg); return PTR_ERR(sdesc); } @@ -77,19 +69,19 @@ static int TSS_sha1(const unsigned char *data, unsigned int datalen, static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, unsigned int keylen, ...) { - struct sdesc *sdesc; + struct tpm_sdesc *sdesc; va_list argp; unsigned int dlen; unsigned char *data; int ret; - sdesc = init_sdesc(hmacalg); + sdesc = tpm_init_sdesc(tpm_hmacalg); if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hmac_alg); + pr_info("Can't alloc %s\n", tpm_hmac_alg); return PTR_ERR(sdesc); } - ret = crypto_shash_setkey(hmacalg, key, keylen); + ret = crypto_shash_setkey(tpm_hmacalg, key, keylen); if (ret < 0) goto out; ret = crypto_shash_init(&sdesc->shash); @@ -126,16 +118,16 @@ static int TSS_authhmac(unsigned char *digest, const unsigned char *key, unsigned char *h2, unsigned char h3, ...) { unsigned char paramdigest[SHA1_DIGEST_SIZE]; - struct sdesc *sdesc; + struct tpm_sdesc *sdesc; unsigned int dlen; unsigned char *data; unsigned char c; int ret; va_list argp; - sdesc = init_sdesc(hashalg); + sdesc = tpm_init_sdesc(tpm_hashalg); if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); + pr_info("Can't alloc %s\n", tpm_hash_alg); return PTR_ERR(sdesc); } @@ -187,7 +179,7 @@ static int TSS_checkhmac1(unsigned char *buffer, unsigned char *authdata; unsigned char testhmac[SHA1_DIGEST_SIZE]; unsigned char paramdigest[SHA1_DIGEST_SIZE]; - struct sdesc *sdesc; + struct tpm_sdesc *sdesc; unsigned int dlen; unsigned int dpos; va_list argp; @@ -205,9 +197,9 @@ static int TSS_checkhmac1(unsigned char *buffer, continueflag = authdata - 1; enonce = continueflag - TPM_NONCE_SIZE; - sdesc = init_sdesc(hashalg); + sdesc = tpm_init_sdesc(tpm_hashalg); if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); + pr_info("Can't alloc %s\n", tpm_hash_alg); return PTR_ERR(sdesc); } ret = crypto_shash_init(&sdesc->shash); @@ -274,7 +266,7 @@ static int TSS_checkhmac2(unsigned char *buffer, unsigned char testhmac1[SHA1_DIGEST_SIZE]; unsigned char testhmac2[SHA1_DIGEST_SIZE]; unsigned char paramdigest[SHA1_DIGEST_SIZE]; - struct sdesc *sdesc; + struct tpm_sdesc *sdesc; unsigned int dlen; unsigned int dpos; va_list argp; @@ -297,9 +289,9 @@ static int TSS_checkhmac2(unsigned char *buffer, enonce1 = continueflag1 - TPM_NONCE_SIZE; enonce2 = continueflag2 - TPM_NONCE_SIZE; - sdesc = init_sdesc(hashalg); + sdesc = tpm_init_sdesc(tpm_hashalg); if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); + pr_info("Can't alloc %s\n", tpm_hash_alg); return PTR_ERR(sdesc); } ret = crypto_shash_init(&sdesc->shash); @@ -355,8 +347,8 @@ out: * For key specific tpm requests, we will generate and send our * own TPM command packets using the drivers send function. */ -static int trusted_tpm_send(struct tpm_chip *chip, unsigned char *cmd, - size_t buflen, const char *desc) +static int tpm_send_dump(struct tpm_chip *chip, + unsigned char *cmd, size_t buflen, const char *desc) { int rc; @@ -372,9 +364,10 @@ static int trusted_tpm_send(struct tpm_chip *chip, unsigned char *cmd, /* * Create an object specific authorisation protocol (OSAP) session */ -static int osap(struct tpm_chip *chip, - struct tpm_buf *tb, struct osapsess *s, - const unsigned char *key, uint16_t type, uint32_t handle) +static int tpm_create_osap(struct tpm_chip *chip, + struct tpm_buf *tb, struct tpm_osapsess *s, + const unsigned char *key, uint16_t type, + uint32_t handle) { unsigned char enonce[TPM_NONCE_SIZE]; unsigned char ononce[TPM_NONCE_SIZE]; @@ -392,8 +385,8 @@ static int osap(struct tpm_chip *chip, store32(tb, handle); storebytes(tb, ononce, TPM_NONCE_SIZE); - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, - "creating OSAP session"); + ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE, + "creating OSAP session"); if (ret < 0) return ret; @@ -409,8 +402,8 @@ static int osap(struct tpm_chip *chip, /* * Create an object independent authorisation protocol (oiap) session */ -static int oiap(struct tpm_chip *chip, struct tpm_buf *tb, uint32_t *handle, - unsigned char *nonce) +static int tpm_create_oiap(struct tpm_chip *chip, struct tpm_buf *tb, + uint32_t *handle, unsigned char *nonce) { int ret; @@ -418,8 +411,8 @@ static int oiap(struct tpm_chip *chip, struct tpm_buf *tb, uint32_t *handle, store16(tb, TPM_TAG_RQU_COMMAND); store32(tb, TPM_OIAP_SIZE); store32(tb, TPM_ORD_OIAP); - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, - "creating OIAP session"); + ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE, + "creating OIAP session"); if (ret < 0) return ret; @@ -448,7 +441,7 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, const unsigned char *blobauth, const unsigned char *pcrinfo, uint32_t pcrinfosize) { - struct osapsess sess; + struct tpm_osapsess sess; struct tpm_digests *td; unsigned char cont; uint32_t ordinal; @@ -466,7 +459,7 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, return -ENOMEM; /* get session for sealing key */ - ret = osap(chip, tb, &sess, keyauth, keytype, keyhandle); + ret = tpm_create_osap(chip, tb, &sess, keyauth, keytype, keyhandle); if (ret < 0) goto out; dump_sess(&sess); @@ -527,8 +520,8 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, store8(tb, cont); storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE); - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, - "sealing data"); + ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE, + "sealing data"); if (ret < 0) goto out; @@ -577,14 +570,14 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, int ret; /* sessions for unsealing key and data */ - ret = oiap(chip, tb, &authhandle1, enonce1); + ret = tpm_create_oiap(chip, tb, &authhandle1, enonce1); if (ret < 0) { - pr_info("trusted_key: oiap failed (%d)\n", ret); + pr_info("Failed to create OIAP 1 (%d)\n", ret); return ret; } - ret = oiap(chip, tb, &authhandle2, enonce2); + ret = tpm_create_oiap(chip, tb, &authhandle2, enonce2); if (ret < 0) { - pr_info("trusted_key: oiap failed (%d)\n", ret); + pr_info("Failed to create OIAP 2 (%d)\n", ret); return ret; } @@ -592,7 +585,7 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, keyhndl = htonl(SRKHANDLE); ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) { - pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); + pr_info("tpm_get_random failed (%d)\n", ret); return ret; } ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, @@ -622,10 +615,10 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, store8(tb, cont); storebytes(tb, authdata2, SHA1_DIGEST_SIZE); - ret = trusted_tpm_send(chip, tb->data, MAX_BUF_SIZE, - "unsealing data"); + ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE, + "unsealing data"); if (ret < 0) { - pr_info("trusted_key: authhmac failed (%d)\n", ret); + pr_info("authhmac failed (%d)\n", ret); return ret; } @@ -637,7 +630,7 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, *datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0, 0); if (ret < 0) { - pr_info("trusted_key: TSS_checkhmac2 failed (%d)\n", ret); + pr_info("TSS_checkhmac2 failed (%d)\n", ret); return ret; } memcpy(data, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *datalen); @@ -645,38 +638,76 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, } EXPORT_SYMBOL_GPL(tpm_unseal); -void trusted_shash_release(void) -{ - if (hashalg) - crypto_free_shash(hashalg); - if (hmacalg) - crypto_free_shash(hmacalg); -} -EXPORT_SYMBOL_GPL(trusted_shash_release); - -int trusted_shash_alloc(void) +/** + * tpm_library_use - Tell the TPM library we want to make use of it + * + * Tell the TPM library that we want to make use of it, allowing it to + * allocate the resources it needs. + */ +int tpm_library_use(void) { + struct crypto_shash *hashalg = NULL; + struct crypto_shash *hmacalg = NULL; int ret; - hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC); + if (atomic_inc_not_zero(&tpm_library_usage)) + return 0; + + /* We don't want to hold a mutex whilst allocating a crypto + * object as it may have to call up to userspace. + */ + hmacalg = crypto_alloc_shash(tpm_hmac_alg, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(hmacalg)) { - pr_info("trusted_key: could not allocate crypto %s\n", - hmac_alg); - return PTR_ERR(hmacalg); + pr_info("Could not allocate crypto %s\n", tpm_hmac_alg); + ret = PTR_ERR(hmacalg); + goto hmacalg_fail; } - hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC); + hashalg = crypto_alloc_shash(tpm_hash_alg, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(hashalg)) { - pr_info("trusted_key: could not allocate crypto %s\n", - hash_alg); + pr_info("Could not allocate crypto %s\n", tpm_hash_alg); ret = PTR_ERR(hashalg); goto hashalg_fail; } + mutex_lock(&tpm_library_init_mutex); + + if (atomic_inc_return(&tpm_library_usage) == 1) { + tpm_hmacalg = hmacalg; + tpm_hashalg = hashalg; + } else { + crypto_free_shash(hashalg); + crypto_free_shash(hmacalg); + } + + mutex_unlock(&tpm_library_init_mutex); return 0; hashalg_fail: - crypto_free_shash(hmacalg); + crypto_free_shash(tpm_hmacalg); +hmacalg_fail: return ret; } -EXPORT_SYMBOL_GPL(trusted_shash_alloc); +EXPORT_SYMBOL_GPL(tpm_library_use); + +/** + * tpm_library_unuse - Tell the TPM library we've finished with it + * + * Tell the TPM library we've finished with it, allowing it to free the + * resources it had allocated. + */ +void tpm_library_unuse(void) +{ + if (atomic_add_unless(&tpm_library_usage, -1, 1)) + return; + + mutex_lock(&tpm_library_init_mutex); + + if (atomic_dec_and_test(&tpm_library_usage)) { + crypto_free_shash(tpm_hashalg); + crypto_free_shash(tpm_hmacalg); + } + + mutex_unlock(&tpm_library_init_mutex); +} +EXPORT_SYMBOL_GPL(tpm_library_unuse); diff --git a/drivers/char/tpm/tpm-library.h b/drivers/char/tpm/tpm-library.h index eec1dfe26c2a..6f7571cc6d6c 100644 --- a/drivers/char/tpm/tpm-library.h +++ b/drivers/char/tpm/tpm-library.h @@ -15,7 +15,7 @@ #define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset]) #define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset])) -struct osapsess { +struct tpm_osapsess { uint32_t handle; unsigned char secret[SHA1_DIGEST_SIZE]; unsigned char enonce[TPM_NONCE_SIZE]; @@ -51,14 +51,14 @@ static inline void storebytes(struct tpm_buf *buf, const unsigned char *in, #define TPM_DEBUG 0 #ifdef TPM_DEBUG -static inline void dump_sess(struct osapsess *s) +static inline void dump_sess(struct tpm_osapsess *s) { - print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE, + print_hex_dump(KERN_INFO, "handle ", DUMP_PREFIX_NONE, 16, 1, &s->handle, 4, 0); - pr_info("trusted-key: secret:\n"); + pr_info("secret:\n"); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, &s->secret, SHA1_DIGEST_SIZE, 0); - pr_info("trusted-key: enonce:\n"); + pr_info("enonce:\n"); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0); } @@ -67,13 +67,13 @@ static inline void dump_tpm_buf(unsigned char *buf) { int len; - pr_info("\ntrusted-key: tpm buffer\n"); + pr_info("\ntpm buffer\n"); len = LOAD32(buf, TPM_SIZE_OFFSET); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0); } #else -static inline void dump_sess(struct osapsess *s) +static inline void dump_sess(struct tpm_osapsess *s) { } diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 5d8caf56c272..b08539920f76 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -100,8 +100,8 @@ struct tpm_buf { #define INIT_BUF(tb) (tb->len = 0) -extern void trusted_shash_release(void); -extern int trusted_shash_alloc(void); +extern int tpm_library_use(void); +extern void tpm_library_unuse(void); extern int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, uint32_t keyhandle, const unsigned char *keyauth, diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 83c6a485e62a..3afb152a6ae2 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -521,19 +521,19 @@ static int __init init_trusted(void) { int ret; - ret = trusted_shash_alloc(); + ret = tpm_library_use(); if (ret < 0) return ret; ret = register_key_type(&key_type_trusted); if (ret < 0) - trusted_shash_release(); + tpm_library_unuse(); return ret; } static void __exit cleanup_trusted(void) { - trusted_shash_release(); unregister_key_type(&key_type_trusted); + tpm_library_unuse(); } late_initcall(init_trusted); |
From: David H. <dho...@re...> - 2018-08-21 15:58:07
|
Better format calls to TSS_rawhmac(), TSS_authhmac() and TSS_checkhmac*(). Each of these calls takes a varargs list of pairs of datasize and data values. Put each pair on its own line so that things are more obvious. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-library.c | 86 ++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/drivers/char/tpm/tpm-library.c b/drivers/char/tpm/tpm-library.c index e4cfc1f090e1..41fe4247a4c8 100644 --- a/drivers/char/tpm/tpm-library.c +++ b/drivers/char/tpm/tpm-library.c @@ -153,9 +153,12 @@ static int TSS_authhmac(unsigned char *digest, const unsigned char *key, if (!ret) ret = crypto_shash_final(&sdesc->shash, paramdigest); if (!ret) - ret = TSS_rawhmac(digest, key, keylen, SHA1_DIGEST_SIZE, - paramdigest, TPM_NONCE_SIZE, h1, - TPM_NONCE_SIZE, h2, 1, &c, 0, 0); + ret = TSS_rawhmac(digest, key, keylen, + SHA1_DIGEST_SIZE, paramdigest, + TPM_NONCE_SIZE, h1, + TPM_NONCE_SIZE, h2, + 1, &c, + 0, 0); out: kfree(sdesc); return ret; @@ -229,9 +232,12 @@ static int TSS_checkhmac1(unsigned char *buffer, if (ret < 0) goto out; - ret = TSS_rawhmac(testhmac, key, keylen, SHA1_DIGEST_SIZE, paramdigest, - TPM_NONCE_SIZE, enonce, TPM_NONCE_SIZE, ononce, - 1, continueflag, 0, 0); + ret = TSS_rawhmac(testhmac, key, keylen, + SHA1_DIGEST_SIZE, paramdigest, + TPM_NONCE_SIZE, enonce, + TPM_NONCE_SIZE, ononce, + 1, continueflag, + 0, 0); if (ret < 0) goto out; @@ -322,18 +328,24 @@ static int TSS_checkhmac2(unsigned char *buffer, if (ret < 0) goto out; - ret = TSS_rawhmac(testhmac1, key1, keylen1, SHA1_DIGEST_SIZE, - paramdigest, TPM_NONCE_SIZE, enonce1, - TPM_NONCE_SIZE, ononce, 1, continueflag1, 0, 0); + ret = TSS_rawhmac(testhmac1, key1, keylen1, + SHA1_DIGEST_SIZE, paramdigest, + TPM_NONCE_SIZE, enonce1, + TPM_NONCE_SIZE, ononce, + 1, continueflag1, + 0, 0); if (ret < 0) goto out; if (memcmp(testhmac1, authdata1, SHA1_DIGEST_SIZE)) { ret = -EINVAL; goto out; } - ret = TSS_rawhmac(testhmac2, key2, keylen2, SHA1_DIGEST_SIZE, - paramdigest, TPM_NONCE_SIZE, enonce2, - TPM_NONCE_SIZE, ononce, 1, continueflag2, 0, 0); + ret = TSS_rawhmac(testhmac2, key2, keylen2, + SHA1_DIGEST_SIZE, paramdigest, + TPM_NONCE_SIZE, enonce2, + TPM_NONCE_SIZE, ononce, + 1, continueflag2, + 0, 0); if (ret < 0) goto out; if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE)) @@ -395,8 +407,10 @@ static int tpm_create_osap(struct tpm_chip *chip, TPM_NONCE_SIZE); memcpy(enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) + TPM_NONCE_SIZE]), TPM_NONCE_SIZE); - return TSS_rawhmac(s->secret, key, SHA1_DIGEST_SIZE, TPM_NONCE_SIZE, - enonce, TPM_NONCE_SIZE, ononce, 0, 0); + return TSS_rawhmac(s->secret, key, SHA1_DIGEST_SIZE, + TPM_NONCE_SIZE, enonce, + TPM_NONCE_SIZE, ononce, + 0, 0); } /* @@ -488,18 +502,23 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, /* no pcr info specified */ ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, sess.enonce, td->nonceodd, cont, - sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, - td->encauth, sizeof(uint32_t), &pcrsize, - sizeof(uint32_t), &datsize, datalen, data, 0, - 0); + sizeof(uint32_t), &ordinal, + SHA1_DIGEST_SIZE, td->encauth, + sizeof(uint32_t), &pcrsize, + sizeof(uint32_t), &datsize, + datalen, data, + 0, 0); } else { /* pcr info specified */ ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, sess.enonce, td->nonceodd, cont, - sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, - td->encauth, sizeof(uint32_t), &pcrsize, - pcrinfosize, pcrinfo, sizeof(uint32_t), - &datsize, datalen, data, 0, 0); + sizeof(uint32_t), &ordinal, + SHA1_DIGEST_SIZE, td->encauth, + sizeof(uint32_t), &pcrsize, + pcrinfosize, pcrinfo, + sizeof(uint32_t), &datsize, + datalen, data, + 0, 0); } if (ret < 0) goto out; @@ -533,9 +552,10 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, sizeof(uint32_t) + encdatasize; /* check the HMAC in the response */ - ret = TSS_checkhmac1(tb->data, ordinal, td->nonceodd, sess.secret, - SHA1_DIGEST_SIZE, storedsize, TPM_DATA_OFFSET, 0, - 0); + ret = TSS_checkhmac1(tb->data, ordinal, td->nonceodd, + sess.secret, SHA1_DIGEST_SIZE, + storedsize, TPM_DATA_OFFSET, + 0, 0); /* copy the returned blob to caller */ if (!ret) { @@ -589,13 +609,17 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, return ret; } ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, - enonce1, nonceodd, cont, sizeof(uint32_t), - &ordinal, bloblen, blob, 0, 0); + enonce1, nonceodd, cont, + sizeof(uint32_t), &ordinal, + bloblen, blob, + 0, 0); if (ret < 0) return ret; ret = TSS_authhmac(authdata2, blobauth, TPM_NONCE_SIZE, - enonce2, nonceodd, cont, sizeof(uint32_t), - &ordinal, bloblen, blob, 0, 0); + enonce2, nonceodd, cont, + sizeof(uint32_t), &ordinal, + bloblen, blob, + 0, 0); if (ret < 0) return ret; @@ -627,8 +651,8 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, keyauth, SHA1_DIGEST_SIZE, blobauth, SHA1_DIGEST_SIZE, sizeof(uint32_t), TPM_DATA_OFFSET, - *datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0, - 0); + *datalen, TPM_DATA_OFFSET + sizeof(uint32_t), + 0, 0); if (ret < 0) { pr_info("TSS_checkhmac2 failed (%d)\n", ret); return ret; |
From: David H. <dho...@re...> - 2018-08-21 15:58:12
|
Put banner comments on public TPM library functions and, if necessary, rename the arguments to make them more obvious. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-library.c | 126 ++++++++++++++++++++++++++++------------ include/linux/tpm.h | 19 ++++++ 2 files changed, 105 insertions(+), 40 deletions(-) diff --git a/drivers/char/tpm/tpm-library.c b/drivers/char/tpm/tpm-library.c index 41fe4247a4c8..7d1f55413f02 100644 --- a/drivers/char/tpm/tpm-library.c +++ b/drivers/char/tpm/tpm-library.c @@ -66,8 +66,17 @@ static int TSS_sha1(const unsigned char *data, unsigned int datalen, return ret; } -static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, - unsigned int keylen, ...) +/** + * TSS_rawhmac - Generate a HMAC(SHA1) from raw data + * @digest: Result buffer - must be SHA1_DIGEST_SIZE in size + * @key: The key to use in the HMAC generation + * @keylen: The size of @key + * @...: Pairs of size and pointer of data elements to load into hmac + * @0,0: Terminator + */ +static int TSS_rawhmac(unsigned char *digest, + const unsigned char *key, unsigned keylen, + ...) { struct tpm_sdesc *sdesc; va_list argp; @@ -110,7 +119,17 @@ out: return ret; } -/* +/** + * TSS_authhmac - Calculate authorisation info to send to TPM + * @digest: Result buffer - must be SHA1_DIGEST_SIZE in size + * @key: The key to use in the HMAC generation + * @keylen: The size of @key + * @h1: Even nonce + * @h2: Odd nonce + * @h3: Continuation flag + * @...: Pairs of size and pointer of data elements to load into hash + * @0,0: Terminator + * * calculate authorization info fields to send to TPM */ static int TSS_authhmac(unsigned char *digest, const unsigned char *key, @@ -378,8 +397,8 @@ static int tpm_send_dump(struct tpm_chip *chip, */ static int tpm_create_osap(struct tpm_chip *chip, struct tpm_buf *tb, struct tpm_osapsess *s, - const unsigned char *key, uint16_t type, - uint32_t handle) + const unsigned char *keyauth, + enum tpm_entity_type keytype, uint32_t keyhandle) { unsigned char enonce[TPM_NONCE_SIZE]; unsigned char ononce[TPM_NONCE_SIZE]; @@ -393,8 +412,8 @@ static int tpm_create_osap(struct tpm_chip *chip, store16(tb, TPM_TAG_RQU_COMMAND); store32(tb, TPM_OSAP_SIZE); store32(tb, TPM_ORD_OSAP); - store16(tb, type); - store32(tb, handle); + store16(tb, keytype); + store32(tb, keyhandle); storebytes(tb, ononce, TPM_NONCE_SIZE); ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE, @@ -407,7 +426,7 @@ static int tpm_create_osap(struct tpm_chip *chip, TPM_NONCE_SIZE); memcpy(enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) + TPM_NONCE_SIZE]), TPM_NONCE_SIZE); - return TSS_rawhmac(s->secret, key, SHA1_DIGEST_SIZE, + return TSS_rawhmac(s->secret, keyauth, SHA1_DIGEST_SIZE, TPM_NONCE_SIZE, enonce, TPM_NONCE_SIZE, ononce, 0, 0); @@ -444,15 +463,33 @@ struct tpm_digests { unsigned char nonceodd[TPM_NONCE_SIZE]; }; -/* - * Have the TPM seal(encrypt) the trusted key, possibly based on - * Platform Configuration Registers (PCRs). AUTH1 for sealing key. +/** + * tpm_seal - Encrypt one key according to another plus PCR state + * @chip: The chip to use + * @tb: Large scratch buffer for I/O + * @keytype: Type of entity attached to @keyhandle + * @keyhandle: TPM-resident key used to encrypt + * @keyauth: 'Password' to use the key. + * @rawdata: Data to be encrypted + * @rawlen: Length of @rawdata + * @encbuffer: Buffer to hold the encrypted data (max SHA1_DIGEST_SIZE) + * @_enclen: Where to place the size of the encrypted data + * @encauth: 'Password' to use to encrypt authorisation key + * @pcrinfo: Information on PCR register values to seal to + * @pcrinfosize: size of @pcrinfo + * + * Have the TPM seal (encrypt) the data in the data buffer. The encryption is + * based on a key already resident in the TPM and may also include the state of + * one or more Platform Configuration Registers (PCRs). + * + * AUTH1 is used for sealing key. */ -int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, +int tpm_seal(struct tpm_chip *chip, + struct tpm_buf *tb, enum tpm_entity_type keytype, uint32_t keyhandle, const unsigned char *keyauth, - const unsigned char *data, uint32_t datalen, - unsigned char *blob, uint32_t *bloblen, - const unsigned char *blobauth, + const unsigned char *rawdata, uint32_t rawlen, + unsigned char *encbuffer, uint32_t *_enclen, + const unsigned char *encauth, const unsigned char *pcrinfo, uint32_t pcrinfosize) { struct tpm_osapsess sess; @@ -489,13 +526,13 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, if (ret != TPM_NONCE_SIZE) goto out; ordinal = htonl(TPM_ORD_SEAL); - datsize = htonl(datalen); + datsize = htonl(rawlen); pcrsize = htonl(pcrinfosize); cont = 0; /* encrypt data authorization key */ for (i = 0; i < SHA1_DIGEST_SIZE; ++i) - td->encauth[i] = td->xorhash[i] ^ blobauth[i]; + td->encauth[i] = td->xorhash[i] ^ encauth[i]; /* calculate authorization HMAC value */ if (pcrinfosize == 0) { @@ -506,7 +543,7 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, SHA1_DIGEST_SIZE, td->encauth, sizeof(uint32_t), &pcrsize, sizeof(uint32_t), &datsize, - datalen, data, + rawlen, rawdata, 0, 0); } else { /* pcr info specified */ @@ -517,7 +554,7 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, sizeof(uint32_t), &pcrsize, pcrinfosize, pcrinfo, sizeof(uint32_t), &datsize, - datalen, data, + rawlen, rawdata, 0, 0); } if (ret < 0) @@ -526,14 +563,14 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, /* build and send the TPM request packet */ INIT_BUF(tb); store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); - store32(tb, TPM_SEAL_SIZE + pcrinfosize + datalen); + store32(tb, TPM_SEAL_SIZE + pcrinfosize + rawlen); store32(tb, TPM_ORD_SEAL); store32(tb, keyhandle); storebytes(tb, td->encauth, SHA1_DIGEST_SIZE); store32(tb, pcrinfosize); storebytes(tb, pcrinfo, pcrinfosize); - store32(tb, datalen); - storebytes(tb, data, datalen); + store32(tb, rawlen); + storebytes(tb, rawdata, rawlen); store32(tb, sess.handle); storebytes(tb, td->nonceodd, TPM_NONCE_SIZE); store8(tb, cont); @@ -544,7 +581,7 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, if (ret < 0) goto out; - /* calculate the size of the returned Blob */ + /* calculate the size of the returned encrypted data */ sealinfosize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t)); encdatasize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t) + sizeof(uint32_t) + sealinfosize); @@ -557,10 +594,10 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, storedsize, TPM_DATA_OFFSET, 0, 0); - /* copy the returned blob to caller */ + /* copy the encrypted data to caller's buffer */ if (!ret) { - memcpy(blob, tb->data + TPM_DATA_OFFSET, storedsize); - *bloblen = storedsize; + memcpy(encbuffer, tb->data + TPM_DATA_OFFSET, storedsize); + *_enclen = storedsize; } out: kfree(td); @@ -568,14 +605,25 @@ out: } EXPORT_SYMBOL_GPL(tpm_seal); -/* +/** + * tpm_unseal - Encrypt one key according to another plus PCR state + * @chip: The chip to use + * @tb: Large scratch buffer for I/O + * @keyhandle: TPM-resident key used to decrypt + * @keyauth: HMAC key + * @encdata: Data to be decrypted + * @enclen: Length of @encdata + * @decauth: Data to use to decrypt the authorisation key + * @rawbuffer: Buffer to hold the decrypted data (max SHA1_DIGEST_SIZE) + * @_rawlen: Where to place the size of the decrypted data + * * use the AUTH2_COMMAND form of unseal, to authorize both key and blob */ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, uint32_t keyhandle, const unsigned char *keyauth, - const unsigned char *blob, int bloblen, - const unsigned char *blobauth, - unsigned char *data, unsigned int *datalen) + const unsigned char *encdata, int enclen, + const unsigned char *decauth, + unsigned char *rawbuffer, unsigned int *_rawlen) { unsigned char nonceodd[TPM_NONCE_SIZE]; unsigned char enonce1[TPM_NONCE_SIZE]; @@ -611,14 +659,14 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, enonce1, nonceodd, cont, sizeof(uint32_t), &ordinal, - bloblen, blob, + enclen, encdata, 0, 0); if (ret < 0) return ret; - ret = TSS_authhmac(authdata2, blobauth, TPM_NONCE_SIZE, + ret = TSS_authhmac(authdata2, decauth, TPM_NONCE_SIZE, enonce2, nonceodd, cont, sizeof(uint32_t), &ordinal, - bloblen, blob, + enclen, encdata, 0, 0); if (ret < 0) return ret; @@ -626,10 +674,10 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, /* build and send TPM request packet */ INIT_BUF(tb); store16(tb, TPM_TAG_RQU_AUTH2_COMMAND); - store32(tb, TPM_UNSEAL_SIZE + bloblen); + store32(tb, TPM_UNSEAL_SIZE + enclen); store32(tb, TPM_ORD_UNSEAL); store32(tb, keyhandle); - storebytes(tb, blob, bloblen); + storebytes(tb, encdata, enclen); store32(tb, authhandle1); storebytes(tb, nonceodd, TPM_NONCE_SIZE); store8(tb, cont); @@ -646,18 +694,18 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, return ret; } - *datalen = LOAD32(tb->data, TPM_DATA_OFFSET); + *_rawlen = LOAD32(tb->data, TPM_DATA_OFFSET); ret = TSS_checkhmac2(tb->data, ordinal, nonceodd, keyauth, SHA1_DIGEST_SIZE, - blobauth, SHA1_DIGEST_SIZE, + decauth, SHA1_DIGEST_SIZE, sizeof(uint32_t), TPM_DATA_OFFSET, - *datalen, TPM_DATA_OFFSET + sizeof(uint32_t), + *_rawlen, TPM_DATA_OFFSET + sizeof(uint32_t), 0, 0); if (ret < 0) { pr_info("TSS_checkhmac2 failed (%d)\n", ret); return ret; } - memcpy(data, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *datalen); + memcpy(rawbuffer, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *_rawlen); return 0; } EXPORT_SYMBOL_GPL(tpm_unseal); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index b08539920f76..cbd13e03a869 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -93,6 +93,22 @@ static inline int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max) { #define TPM_RETURN_OFFSET 6 #define TPM_DATA_OFFSET 10 +enum tpm_entity_type { + TPM_ET_KEYHANDLE = 0x01, + TPM_ET_OWNER = 0x02, + TPM_ET_DATA = 0x03, + TPM_ET_SRK = 0x04, + TPM_ET_KEY = 0x05, + TPM_ET_REVOKE = 0x06, + TPM_ET_DEL_OWNER_BLOB = 0x07, + TPM_ET_DEL_ROW = 0x08, + TPM_ET_DEL_KEY_BLOB = 0x09, + TPM_ET_COUNTER = 0x0a, + TPM_ET_NV = 0x0b, + TPM_ET_OPERATOR = 0x0c, + TPM_ET_RESERVED_HANDLE = 0x40, +}; + struct tpm_buf { int len; unsigned char data[MAX_BUF_SIZE]; @@ -103,7 +119,8 @@ struct tpm_buf { extern int tpm_library_use(void); extern void tpm_library_unuse(void); -extern int tpm_seal(struct tpm_chip *chip, struct tpm_buf *tb, uint16_t keytype, +extern int tpm_seal(struct tpm_chip *chip, + struct tpm_buf *tb, enum tpm_entity_type keytype, uint32_t keyhandle, const unsigned char *keyauth, const unsigned char *data, uint32_t datalen, unsigned char *blob, uint32_t *bloblen, |
From: David H. <dho...@re...> - 2018-08-21 15:58:20
|
Create a tpm_even_nonce struct and a tpm_odd_nonce struct to represent nonces in internal routines rather than directly using char arrays. Having two separate structures helps keep track of which nonce is used where. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-library.c | 151 +++++++++++++++++++++------------------- drivers/char/tpm/tpm-library.h | 10 ++- 2 files changed, 90 insertions(+), 71 deletions(-) diff --git a/drivers/char/tpm/tpm-library.c b/drivers/char/tpm/tpm-library.c index 7d1f55413f02..1c64282d1d0d 100644 --- a/drivers/char/tpm/tpm-library.c +++ b/drivers/char/tpm/tpm-library.c @@ -35,6 +35,19 @@ static atomic_t tpm_library_usage; static struct crypto_shash *tpm_hashalg; static struct crypto_shash *tpm_hmacalg; +static int tpm_gen_odd_nonce(struct tpm_chip *chip, + struct tpm_odd_nonce *ononce) +{ + int ret; + + ret = tpm_get_random(chip, ononce->data, TPM_NONCE_SIZE); + if (ret == TPM_NONCE_SIZE) + ret = 0; + else + pr_info("tpm_get_random failed (%d)\n", ret); + return ret; +} + static struct tpm_sdesc *tpm_init_sdesc(struct crypto_shash *alg) { struct tpm_sdesc *sdesc; @@ -124,23 +137,25 @@ out: * @digest: Result buffer - must be SHA1_DIGEST_SIZE in size * @key: The key to use in the HMAC generation * @keylen: The size of @key - * @h1: Even nonce - * @h2: Odd nonce - * @h3: Continuation flag + * @enonce: Even nonce + * @ononce: Odd nonce + * @cont: Continuation flag * @...: Pairs of size and pointer of data elements to load into hash * @0,0: Terminator * * calculate authorization info fields to send to TPM */ -static int TSS_authhmac(unsigned char *digest, const unsigned char *key, - unsigned int keylen, unsigned char *h1, - unsigned char *h2, unsigned char h3, ...) +static int TSS_authhmac(unsigned char *digest, + const unsigned char *key, unsigned keylen, + const struct tpm_even_nonce *enonce, + const struct tpm_odd_nonce *ononce, + unsigned char cont, + ...) { unsigned char paramdigest[SHA1_DIGEST_SIZE]; struct tpm_sdesc *sdesc; unsigned int dlen; unsigned char *data; - unsigned char c; int ret; va_list argp; @@ -150,11 +165,10 @@ static int TSS_authhmac(unsigned char *digest, const unsigned char *key, return PTR_ERR(sdesc); } - c = h3; ret = crypto_shash_init(&sdesc->shash); if (ret < 0) goto out; - va_start(argp, h3); + va_start(argp, cont); for (;;) { dlen = va_arg(argp, unsigned int); if (dlen == 0) @@ -174,9 +188,9 @@ static int TSS_authhmac(unsigned char *digest, const unsigned char *key, if (!ret) ret = TSS_rawhmac(digest, key, keylen, SHA1_DIGEST_SIZE, paramdigest, - TPM_NONCE_SIZE, h1, - TPM_NONCE_SIZE, h2, - 1, &c, + TPM_NONCE_SIZE, enonce->data, + TPM_NONCE_SIZE, ononce->data, + 1, &cont, 0, 0); out: kfree(sdesc); @@ -188,15 +202,15 @@ out: */ static int TSS_checkhmac1(unsigned char *buffer, const uint32_t command, - const unsigned char *ononce, - const unsigned char *key, - unsigned int keylen, ...) + const struct tpm_odd_nonce *ononce, + const unsigned char *key, unsigned keylen, + ...) { uint32_t bufsize; uint16_t tag; uint32_t ordinal; uint32_t result; - unsigned char *enonce; + struct tpm_even_nonce *enonce; unsigned char *continueflag; unsigned char *authdata; unsigned char testhmac[SHA1_DIGEST_SIZE]; @@ -217,7 +231,7 @@ static int TSS_checkhmac1(unsigned char *buffer, return -EINVAL; authdata = buffer + bufsize - SHA1_DIGEST_SIZE; continueflag = authdata - 1; - enonce = continueflag - TPM_NONCE_SIZE; + enonce = (void *)continueflag - TPM_NONCE_SIZE; sdesc = tpm_init_sdesc(tpm_hashalg); if (IS_ERR(sdesc)) { @@ -253,8 +267,8 @@ static int TSS_checkhmac1(unsigned char *buffer, ret = TSS_rawhmac(testhmac, key, keylen, SHA1_DIGEST_SIZE, paramdigest, - TPM_NONCE_SIZE, enonce, - TPM_NONCE_SIZE, ononce, + TPM_NONCE_SIZE, enonce->data, + TPM_NONCE_SIZE, ononce->data, 1, continueflag, 0, 0); if (ret < 0) @@ -270,24 +284,23 @@ out: /* * verify the AUTH2_COMMAND (unseal) result from TPM */ -static int TSS_checkhmac2(unsigned char *buffer, +static int TSS_checkhmac2(const unsigned char *buffer, const uint32_t command, - const unsigned char *ononce, - const unsigned char *key1, - unsigned int keylen1, - const unsigned char *key2, - unsigned int keylen2, ...) + const struct tpm_odd_nonce *ononce, + const unsigned char *key1, unsigned keylen1, + const unsigned char *key2, unsigned keylen2, + ...) { uint32_t bufsize; uint16_t tag; uint32_t ordinal; uint32_t result; - unsigned char *enonce1; - unsigned char *continueflag1; - unsigned char *authdata1; - unsigned char *enonce2; - unsigned char *continueflag2; - unsigned char *authdata2; + const struct tpm_even_nonce *enonce1; + const unsigned char *continueflag1; + const unsigned char *authdata1; + const struct tpm_even_nonce *enonce2; + const unsigned char *continueflag2; + const unsigned char *authdata2; unsigned char testhmac1[SHA1_DIGEST_SIZE]; unsigned char testhmac2[SHA1_DIGEST_SIZE]; unsigned char paramdigest[SHA1_DIGEST_SIZE]; @@ -311,8 +324,8 @@ static int TSS_checkhmac2(unsigned char *buffer, authdata2 = buffer + bufsize - (SHA1_DIGEST_SIZE); continueflag1 = authdata1 - 1; continueflag2 = authdata2 - 1; - enonce1 = continueflag1 - TPM_NONCE_SIZE; - enonce2 = continueflag2 - TPM_NONCE_SIZE; + enonce1 = (const void *)continueflag1 - TPM_NONCE_SIZE; + enonce2 = (const void *)continueflag2 - TPM_NONCE_SIZE; sdesc = tpm_init_sdesc(tpm_hashalg); if (IS_ERR(sdesc)) { @@ -349,8 +362,8 @@ static int TSS_checkhmac2(unsigned char *buffer, ret = TSS_rawhmac(testhmac1, key1, keylen1, SHA1_DIGEST_SIZE, paramdigest, - TPM_NONCE_SIZE, enonce1, - TPM_NONCE_SIZE, ononce, + TPM_NONCE_SIZE, enonce1->data, + TPM_NONCE_SIZE, ononce->data, 1, continueflag1, 0, 0); if (ret < 0) @@ -361,8 +374,8 @@ static int TSS_checkhmac2(unsigned char *buffer, } ret = TSS_rawhmac(testhmac2, key2, keylen2, SHA1_DIGEST_SIZE, paramdigest, - TPM_NONCE_SIZE, enonce2, - TPM_NONCE_SIZE, ononce, + TPM_NONCE_SIZE, enonce2->data, + TPM_NONCE_SIZE, ononce->data, 1, continueflag2, 0, 0); if (ret < 0) @@ -400,12 +413,12 @@ static int tpm_create_osap(struct tpm_chip *chip, const unsigned char *keyauth, enum tpm_entity_type keytype, uint32_t keyhandle) { - unsigned char enonce[TPM_NONCE_SIZE]; - unsigned char ononce[TPM_NONCE_SIZE]; + struct tpm_even_nonce enonce; + struct tpm_odd_nonce ononce; int ret; - ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE); - if (ret != TPM_NONCE_SIZE) + ret = tpm_gen_odd_nonce(chip, &ononce); + if (ret < 0) return ret; INIT_BUF(tb); @@ -414,7 +427,7 @@ static int tpm_create_osap(struct tpm_chip *chip, store32(tb, TPM_ORD_OSAP); store16(tb, keytype); store32(tb, keyhandle); - storebytes(tb, ononce, TPM_NONCE_SIZE); + storebytes(tb, ononce.data, TPM_NONCE_SIZE); ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE, "creating OSAP session"); @@ -422,13 +435,13 @@ static int tpm_create_osap(struct tpm_chip *chip, return ret; s->handle = LOAD32(tb->data, TPM_DATA_OFFSET); - memcpy(s->enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)]), + memcpy(s->enonce.data, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)]), TPM_NONCE_SIZE); - memcpy(enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) + + memcpy(enonce.data, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) + TPM_NONCE_SIZE]), TPM_NONCE_SIZE); return TSS_rawhmac(s->secret, keyauth, SHA1_DIGEST_SIZE, - TPM_NONCE_SIZE, enonce, - TPM_NONCE_SIZE, ononce, + TPM_NONCE_SIZE, enonce.data, + TPM_NONCE_SIZE, ononce.data, 0, 0); } @@ -436,7 +449,7 @@ static int tpm_create_osap(struct tpm_chip *chip, * Create an object independent authorisation protocol (oiap) session */ static int tpm_create_oiap(struct tpm_chip *chip, struct tpm_buf *tb, - uint32_t *handle, unsigned char *nonce) + uint32_t *handle, struct tpm_even_nonce *enonce) { int ret; @@ -450,7 +463,7 @@ static int tpm_create_oiap(struct tpm_chip *chip, struct tpm_buf *tb, return ret; *handle = LOAD32(tb->data, TPM_DATA_OFFSET); - memcpy(nonce, &tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)], + memcpy(enonce->data, &tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)], TPM_NONCE_SIZE); return 0; } @@ -460,7 +473,7 @@ struct tpm_digests { unsigned char pubauth[SHA1_DIGEST_SIZE]; unsigned char xorwork[SHA1_DIGEST_SIZE * 2]; unsigned char xorhash[SHA1_DIGEST_SIZE]; - unsigned char nonceodd[TPM_NONCE_SIZE]; + struct tpm_odd_nonce ononce; }; /** @@ -517,13 +530,13 @@ int tpm_seal(struct tpm_chip *chip, /* calculate encrypted authorization value */ memcpy(td->xorwork, sess.secret, SHA1_DIGEST_SIZE); - memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE); + memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce.data, SHA1_DIGEST_SIZE); ret = TSS_sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash); if (ret < 0) goto out; - ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE); - if (ret != TPM_NONCE_SIZE) + ret = tpm_gen_odd_nonce(chip, &td->ononce); + if (ret < 0) goto out; ordinal = htonl(TPM_ORD_SEAL); datsize = htonl(rawlen); @@ -538,7 +551,7 @@ int tpm_seal(struct tpm_chip *chip, if (pcrinfosize == 0) { /* no pcr info specified */ ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, - sess.enonce, td->nonceodd, cont, + &sess.enonce, &td->ononce, cont, sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, td->encauth, sizeof(uint32_t), &pcrsize, @@ -548,7 +561,7 @@ int tpm_seal(struct tpm_chip *chip, } else { /* pcr info specified */ ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, - sess.enonce, td->nonceodd, cont, + &sess.enonce, &td->ononce, cont, sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, td->encauth, sizeof(uint32_t), &pcrsize, @@ -572,7 +585,7 @@ int tpm_seal(struct tpm_chip *chip, store32(tb, rawlen); storebytes(tb, rawdata, rawlen); store32(tb, sess.handle); - storebytes(tb, td->nonceodd, TPM_NONCE_SIZE); + storebytes(tb, td->ononce.data, TPM_NONCE_SIZE); store8(tb, cont); storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE); @@ -589,7 +602,7 @@ int tpm_seal(struct tpm_chip *chip, sizeof(uint32_t) + encdatasize; /* check the HMAC in the response */ - ret = TSS_checkhmac1(tb->data, ordinal, td->nonceodd, + ret = TSS_checkhmac1(tb->data, ordinal, &td->ononce, sess.secret, SHA1_DIGEST_SIZE, storedsize, TPM_DATA_OFFSET, 0, 0); @@ -625,9 +638,9 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, const unsigned char *decauth, unsigned char *rawbuffer, unsigned int *_rawlen) { - unsigned char nonceodd[TPM_NONCE_SIZE]; - unsigned char enonce1[TPM_NONCE_SIZE]; - unsigned char enonce2[TPM_NONCE_SIZE]; + struct tpm_odd_nonce ononce; + struct tpm_even_nonce enonce1; + struct tpm_even_nonce enonce2; unsigned char authdata1[SHA1_DIGEST_SIZE]; unsigned char authdata2[SHA1_DIGEST_SIZE]; uint32_t authhandle1 = 0; @@ -638,12 +651,12 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, int ret; /* sessions for unsealing key and data */ - ret = tpm_create_oiap(chip, tb, &authhandle1, enonce1); + ret = tpm_create_oiap(chip, tb, &authhandle1, &enonce1); if (ret < 0) { pr_info("Failed to create OIAP 1 (%d)\n", ret); return ret; } - ret = tpm_create_oiap(chip, tb, &authhandle2, enonce2); + ret = tpm_create_oiap(chip, tb, &authhandle2, &enonce2); if (ret < 0) { pr_info("Failed to create OIAP 2 (%d)\n", ret); return ret; @@ -651,20 +664,18 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, ordinal = htonl(TPM_ORD_UNSEAL); keyhndl = htonl(SRKHANDLE); - ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE); - if (ret != TPM_NONCE_SIZE) { - pr_info("tpm_get_random failed (%d)\n", ret); + ret = tpm_gen_odd_nonce(chip, &ononce); + if (ret < 0) return ret; - } ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, - enonce1, nonceodd, cont, + &enonce1, &ononce, cont, sizeof(uint32_t), &ordinal, enclen, encdata, 0, 0); if (ret < 0) return ret; ret = TSS_authhmac(authdata2, decauth, TPM_NONCE_SIZE, - enonce2, nonceodd, cont, + &enonce2, &ononce, cont, sizeof(uint32_t), &ordinal, enclen, encdata, 0, 0); @@ -679,11 +690,11 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, store32(tb, keyhandle); storebytes(tb, encdata, enclen); store32(tb, authhandle1); - storebytes(tb, nonceodd, TPM_NONCE_SIZE); + storebytes(tb, ononce.data, TPM_NONCE_SIZE); store8(tb, cont); storebytes(tb, authdata1, SHA1_DIGEST_SIZE); store32(tb, authhandle2); - storebytes(tb, nonceodd, TPM_NONCE_SIZE); + storebytes(tb, ononce.data, TPM_NONCE_SIZE); store8(tb, cont); storebytes(tb, authdata2, SHA1_DIGEST_SIZE); @@ -695,7 +706,7 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, } *_rawlen = LOAD32(tb->data, TPM_DATA_OFFSET); - ret = TSS_checkhmac2(tb->data, ordinal, nonceodd, + ret = TSS_checkhmac2(tb->data, ordinal, &ononce, keyauth, SHA1_DIGEST_SIZE, decauth, SHA1_DIGEST_SIZE, sizeof(uint32_t), TPM_DATA_OFFSET, diff --git a/drivers/char/tpm/tpm-library.h b/drivers/char/tpm/tpm-library.h index 6f7571cc6d6c..ceb0ea1cd2bb 100644 --- a/drivers/char/tpm/tpm-library.h +++ b/drivers/char/tpm/tpm-library.h @@ -15,10 +15,18 @@ #define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset]) #define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset])) +struct tpm_even_nonce { + unsigned char data[TPM_NONCE_SIZE]; +}; + +struct tpm_odd_nonce { + unsigned char data[TPM_NONCE_SIZE]; +}; + struct tpm_osapsess { uint32_t handle; unsigned char secret[SHA1_DIGEST_SIZE]; - unsigned char enonce[TPM_NONCE_SIZE]; + struct tpm_even_nonce enonce; }; static inline void store8(struct tpm_buf *buf, const unsigned char value) |
From: David H. <dho...@re...> - 2018-08-21 15:58:26
|
Rename store8() and storebytes() so that the names are the same length as for store16() and store32() and their parameters line up, making blocks of them easier to read. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-library.c | 28 ++++++++++++++-------------- drivers/char/tpm/tpm-library.h | 14 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/char/tpm/tpm-library.c b/drivers/char/tpm/tpm-library.c index 1c64282d1d0d..1eea483cf36b 100644 --- a/drivers/char/tpm/tpm-library.c +++ b/drivers/char/tpm/tpm-library.c @@ -427,7 +427,7 @@ static int tpm_create_osap(struct tpm_chip *chip, store32(tb, TPM_ORD_OSAP); store16(tb, keytype); store32(tb, keyhandle); - storebytes(tb, ononce.data, TPM_NONCE_SIZE); + store_s(tb, ononce.data, TPM_NONCE_SIZE); ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE, "creating OSAP session"); @@ -579,15 +579,15 @@ int tpm_seal(struct tpm_chip *chip, store32(tb, TPM_SEAL_SIZE + pcrinfosize + rawlen); store32(tb, TPM_ORD_SEAL); store32(tb, keyhandle); - storebytes(tb, td->encauth, SHA1_DIGEST_SIZE); + store_s(tb, td->encauth, SHA1_DIGEST_SIZE); store32(tb, pcrinfosize); - storebytes(tb, pcrinfo, pcrinfosize); + store_s(tb, pcrinfo, pcrinfosize); store32(tb, rawlen); - storebytes(tb, rawdata, rawlen); + store_s(tb, rawdata, rawlen); store32(tb, sess.handle); - storebytes(tb, td->ononce.data, TPM_NONCE_SIZE); - store8(tb, cont); - storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE); + store_s(tb, td->ononce.data, TPM_NONCE_SIZE); + store_8(tb, cont); + store_s(tb, td->pubauth, SHA1_DIGEST_SIZE); ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE, "sealing data"); @@ -688,15 +688,15 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, store32(tb, TPM_UNSEAL_SIZE + enclen); store32(tb, TPM_ORD_UNSEAL); store32(tb, keyhandle); - storebytes(tb, encdata, enclen); + store_s(tb, encdata, enclen); store32(tb, authhandle1); - storebytes(tb, ononce.data, TPM_NONCE_SIZE); - store8(tb, cont); - storebytes(tb, authdata1, SHA1_DIGEST_SIZE); + store_s(tb, ononce.data, TPM_NONCE_SIZE); + store_8(tb, cont); + store_s(tb, authdata1, SHA1_DIGEST_SIZE); store32(tb, authhandle2); - storebytes(tb, ononce.data, TPM_NONCE_SIZE); - store8(tb, cont); - storebytes(tb, authdata2, SHA1_DIGEST_SIZE); + store_s(tb, ononce.data, TPM_NONCE_SIZE); + store_8(tb, cont); + store_s(tb, authdata2, SHA1_DIGEST_SIZE); ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE, "unsealing data"); diff --git a/drivers/char/tpm/tpm-library.h b/drivers/char/tpm/tpm-library.h index ceb0ea1cd2bb..c1af99dba08d 100644 --- a/drivers/char/tpm/tpm-library.h +++ b/drivers/char/tpm/tpm-library.h @@ -29,25 +29,25 @@ struct tpm_osapsess { struct tpm_even_nonce enonce; }; -static inline void store8(struct tpm_buf *buf, const unsigned char value) +static inline void store_8(struct tpm_buf *buf, unsigned char value) { buf->data[buf->len++] = value; } -static inline void store16(struct tpm_buf *buf, const uint16_t value) +static inline void store16(struct tpm_buf *buf, uint16_t value) { - *(uint16_t *) & buf->data[buf->len] = htons(value); + *(uint16_t *)&buf->data[buf->len] = htons(value); buf->len += sizeof value; } -static inline void store32(struct tpm_buf *buf, const uint32_t value) +static inline void store32(struct tpm_buf *buf, uint32_t value) { - *(uint32_t *) & buf->data[buf->len] = htonl(value); + *(uint32_t *)&buf->data[buf->len] = htonl(value); buf->len += sizeof value; } -static inline void storebytes(struct tpm_buf *buf, const unsigned char *in, - const int len) +static inline void store_s(struct tpm_buf *buf, const unsigned char *in, + int len) { memcpy(buf->data + buf->len, in, len); buf->len += len; |
From: David H. <dho...@re...> - 2018-08-21 15:58:32
|
Make store_s() (store arbitrarily-sized data) take a void* data argument, not unsigned char* so that it can be given a pointer to a structure to be included without the need to cast. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-library.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm-library.h b/drivers/char/tpm/tpm-library.h index c1af99dba08d..861e67df27fc 100644 --- a/drivers/char/tpm/tpm-library.h +++ b/drivers/char/tpm/tpm-library.h @@ -46,8 +46,7 @@ static inline void store32(struct tpm_buf *buf, uint32_t value) buf->len += sizeof value; } -static inline void store_s(struct tpm_buf *buf, const unsigned char *in, - int len) +static inline void store_s(struct tpm_buf *buf, const void *in, int len) { memcpy(buf->data + buf->len, in, len); buf->len += len; |
From: David H. <dho...@re...> - 2018-08-21 15:58:40
|
Use __be32 rather than int32_t and use cpu_to_beX() and co. rather than htonl() and co. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-library.c | 58 ++++++++++++++++++---------------------- drivers/char/tpm/tpm-library.h | 14 +++++----- 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/drivers/char/tpm/tpm-library.c b/drivers/char/tpm/tpm-library.c index 1eea483cf36b..f14980be5ebb 100644 --- a/drivers/char/tpm/tpm-library.c +++ b/drivers/char/tpm/tpm-library.c @@ -201,15 +201,14 @@ out: * verify the AUTH1_COMMAND (Seal) result from TPM */ static int TSS_checkhmac1(unsigned char *buffer, - const uint32_t command, + __be32 ordinal, const struct tpm_odd_nonce *ononce, const unsigned char *key, unsigned keylen, ...) { uint32_t bufsize; uint16_t tag; - uint32_t ordinal; - uint32_t result; + __be32 result; struct tpm_even_nonce *enonce; unsigned char *continueflag; unsigned char *authdata; @@ -223,8 +222,7 @@ static int TSS_checkhmac1(unsigned char *buffer, bufsize = LOAD32(buffer, TPM_SIZE_OFFSET); tag = LOAD16(buffer, 0); - ordinal = command; - result = LOAD32N(buffer, TPM_RETURN_OFFSET); + result = LOAD32BE(buffer, TPM_RETURN_OFFSET); if (tag == TPM_TAG_RSP_COMMAND) return 0; if (tag != TPM_TAG_RSP_AUTH1_COMMAND) @@ -246,7 +244,7 @@ static int TSS_checkhmac1(unsigned char *buffer, if (ret < 0) goto out; ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, - sizeof ordinal); + sizeof(ordinal)); if (ret < 0) goto out; va_start(argp, keylen); @@ -285,7 +283,7 @@ out: * verify the AUTH2_COMMAND (unseal) result from TPM */ static int TSS_checkhmac2(const unsigned char *buffer, - const uint32_t command, + __be32 ordinal, const struct tpm_odd_nonce *ononce, const unsigned char *key1, unsigned keylen1, const unsigned char *key2, unsigned keylen2, @@ -293,8 +291,7 @@ static int TSS_checkhmac2(const unsigned char *buffer, { uint32_t bufsize; uint16_t tag; - uint32_t ordinal; - uint32_t result; + __be32 result; const struct tpm_even_nonce *enonce1; const unsigned char *continueflag1; const unsigned char *authdata1; @@ -312,8 +309,7 @@ static int TSS_checkhmac2(const unsigned char *buffer, bufsize = LOAD32(buffer, TPM_SIZE_OFFSET); tag = LOAD16(buffer, 0); - ordinal = command; - result = LOAD32N(buffer, TPM_RETURN_OFFSET); + result = LOAD32BE(buffer, TPM_RETURN_OFFSET); if (tag == TPM_TAG_RSP_COMMAND) return 0; @@ -336,11 +332,11 @@ static int TSS_checkhmac2(const unsigned char *buffer, if (ret < 0) goto out; ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, - sizeof result); + sizeof(result)); if (ret < 0) goto out; ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, - sizeof ordinal); + sizeof(ordinal)); if (ret < 0) goto out; @@ -508,9 +504,9 @@ int tpm_seal(struct tpm_chip *chip, struct tpm_osapsess sess; struct tpm_digests *td; unsigned char cont; - uint32_t ordinal; - uint32_t pcrsize; - uint32_t datsize; + __be32 ordinal_be; + __be32 rawlen_be; + __be32 pcrinfosize_be; int sealinfosize; int encdatasize; int storedsize; @@ -538,9 +534,9 @@ int tpm_seal(struct tpm_chip *chip, ret = tpm_gen_odd_nonce(chip, &td->ononce); if (ret < 0) goto out; - ordinal = htonl(TPM_ORD_SEAL); - datsize = htonl(rawlen); - pcrsize = htonl(pcrinfosize); + ordinal_be = cpu_to_be32(TPM_ORD_SEAL); + rawlen_be = cpu_to_be32(rawlen); + pcrinfosize_be = cpu_to_be32(pcrinfosize); cont = 0; /* encrypt data authorization key */ @@ -552,21 +548,21 @@ int tpm_seal(struct tpm_chip *chip, /* no pcr info specified */ ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, &sess.enonce, &td->ononce, cont, - sizeof(uint32_t), &ordinal, + sizeof(__be32), &ordinal_be, SHA1_DIGEST_SIZE, td->encauth, - sizeof(uint32_t), &pcrsize, - sizeof(uint32_t), &datsize, + sizeof(__be32), &pcrinfosize_be, + sizeof(__be32), &rawlen_be, rawlen, rawdata, 0, 0); } else { /* pcr info specified */ ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, &sess.enonce, &td->ononce, cont, - sizeof(uint32_t), &ordinal, + sizeof(__be32), &ordinal_be, SHA1_DIGEST_SIZE, td->encauth, - sizeof(uint32_t), &pcrsize, + sizeof(__be32), &pcrinfosize_be, pcrinfosize, pcrinfo, - sizeof(uint32_t), &datsize, + sizeof(__be32), &rawlen_be, rawlen, rawdata, 0, 0); } @@ -602,7 +598,7 @@ int tpm_seal(struct tpm_chip *chip, sizeof(uint32_t) + encdatasize; /* check the HMAC in the response */ - ret = TSS_checkhmac1(tb->data, ordinal, &td->ononce, + ret = TSS_checkhmac1(tb->data, ordinal_be, &td->ononce, sess.secret, SHA1_DIGEST_SIZE, storedsize, TPM_DATA_OFFSET, 0, 0); @@ -646,8 +642,7 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, uint32_t authhandle1 = 0; uint32_t authhandle2 = 0; unsigned char cont = 0; - uint32_t ordinal; - uint32_t keyhndl; + __be32 ordinal; int ret; /* sessions for unsealing key and data */ @@ -662,21 +657,20 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, return ret; } - ordinal = htonl(TPM_ORD_UNSEAL); - keyhndl = htonl(SRKHANDLE); + ordinal = cpu_to_be32(TPM_ORD_UNSEAL); ret = tpm_gen_odd_nonce(chip, &ononce); if (ret < 0) return ret; ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, &enonce1, &ononce, cont, - sizeof(uint32_t), &ordinal, + sizeof(__be32), &ordinal, enclen, encdata, 0, 0); if (ret < 0) return ret; ret = TSS_authhmac(authdata2, decauth, TPM_NONCE_SIZE, &enonce2, &ononce, cont, - sizeof(uint32_t), &ordinal, + sizeof(__be32), &ordinal, enclen, encdata, 0, 0); if (ret < 0) diff --git a/drivers/char/tpm/tpm-library.h b/drivers/char/tpm/tpm-library.h index 861e67df27fc..c12d451704a2 100644 --- a/drivers/char/tpm/tpm-library.h +++ b/drivers/char/tpm/tpm-library.h @@ -11,9 +11,9 @@ */ -#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset])) -#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset]) -#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset])) +#define LOAD32BE(buffer, offset) (*(__be32 *)&buffer[(offset)]) +#define LOAD16(buffer, offset) (be16_to_cpu(*(__be16 *)&buffer[(offset)])) +#define LOAD32(buffer, offset) (be32_to_cpu(LOAD32BE(buffer, (offset)))) struct tpm_even_nonce { unsigned char data[TPM_NONCE_SIZE]; @@ -36,14 +36,14 @@ static inline void store_8(struct tpm_buf *buf, unsigned char value) static inline void store16(struct tpm_buf *buf, uint16_t value) { - *(uint16_t *)&buf->data[buf->len] = htons(value); - buf->len += sizeof value; + *(__be16 *)&buf->data[buf->len] = cpu_to_be16(value); + buf->len += sizeof(value); } static inline void store32(struct tpm_buf *buf, uint32_t value) { - *(uint32_t *)&buf->data[buf->len] = htonl(value); - buf->len += sizeof value; + *(__be32 *)&buf->data[buf->len] = cpu_to_be32(value); + buf->len += sizeof(value); } static inline void store_s(struct tpm_buf *buf, const void *in, int len) |
From: David H. <dho...@re...> - 2018-08-21 15:58:48
|
Put more comments into the HMAC generation functions in the TPM call library so that it's easier to see how it relates to the request and response tables in the documents. Signed-off-by: David Howells <dho...@re...> --- drivers/char/tpm/tpm-library.c | 161 +++++++++++++++++++++------------------- 1 file changed, 83 insertions(+), 78 deletions(-) diff --git a/drivers/char/tpm/tpm-library.c b/drivers/char/tpm/tpm-library.c index f14980be5ebb..46cd12d30ec6 100644 --- a/drivers/char/tpm/tpm-library.c +++ b/drivers/char/tpm/tpm-library.c @@ -85,7 +85,7 @@ static int TSS_sha1(const unsigned char *data, unsigned int datalen, * @key: The key to use in the HMAC generation * @keylen: The size of @key * @...: Pairs of size and pointer of data elements to load into hmac - * @0,0: Terminator + * @0,NULL: Terminator */ static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, unsigned keylen, @@ -113,13 +113,9 @@ static int TSS_rawhmac(unsigned char *digest, va_start(argp, keylen); for (;;) { dlen = va_arg(argp, unsigned int); - if (dlen == 0) - break; data = va_arg(argp, unsigned char *); - if (data == NULL) { - ret = -EINVAL; + if (!data) break; - } ret = crypto_shash_update(&sdesc->shash, data, dlen); if (ret < 0) break; @@ -141,9 +137,9 @@ out: * @ononce: Odd nonce * @cont: Continuation flag * @...: Pairs of size and pointer of data elements to load into hash - * @0,0: Terminator + * @0,NULL: Terminator * - * calculate authorization info fields to send to TPM + * Calculate authorization info fields to send to TPM */ static int TSS_authhmac(unsigned char *digest, const unsigned char *key, unsigned keylen, @@ -171,13 +167,9 @@ static int TSS_authhmac(unsigned char *digest, va_start(argp, cont); for (;;) { dlen = va_arg(argp, unsigned int); - if (dlen == 0) - break; data = va_arg(argp, unsigned char *); - if (!data) { - ret = -EINVAL; + if (!data) break; - } ret = crypto_shash_update(&sdesc->shash, data, dlen); if (ret < 0) break; @@ -187,18 +179,25 @@ static int TSS_authhmac(unsigned char *digest, ret = crypto_shash_final(&sdesc->shash, paramdigest); if (!ret) ret = TSS_rawhmac(digest, key, keylen, - SHA1_DIGEST_SIZE, paramdigest, - TPM_NONCE_SIZE, enonce->data, - TPM_NONCE_SIZE, ononce->data, - 1, &cont, - 0, 0); + /* 1H1 */ SHA1_DIGEST_SIZE, paramdigest, + /* 2H1 */ TPM_NONCE_SIZE, enonce->data, + /* 3H1 */ TPM_NONCE_SIZE, ononce->data, + /* 4H1 */ 1, &cont, + 0, NULL); out: kfree(sdesc); return ret; } -/* - * verify the AUTH1_COMMAND (Seal) result from TPM +/** + * TSS_checkhmac1 - Verify the result of an AUTH1_COMMAND (eg. Seal) + * @digest: Reply buffer + * @ordinal: The command ID, BE form + * @ononce: Odd nonce + * @key: The key to use in the HMAC generation + * @keylen: The size of @key + * @...: Pairs of size and pointer of data elements to load into hash + * @0,NULL: Terminator */ static int TSS_checkhmac1(unsigned char *buffer, __be32 ordinal, @@ -231,6 +230,9 @@ static int TSS_checkhmac1(unsigned char *buffer, continueflag = authdata - 1; enonce = (void *)continueflag - TPM_NONCE_SIZE; + /* Load the 1S, 2S, 3S, ... marked fields into a hash. The digest + * value is then 1H1 loaded into the HMAC below. + */ sdesc = tpm_init_sdesc(tpm_hashalg); if (IS_ERR(sdesc)) { pr_info("Can't alloc %s\n", tpm_hash_alg); @@ -240,19 +242,19 @@ static int TSS_checkhmac1(unsigned char *buffer, if (ret < 0) goto out; ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, - sizeof result); + sizeof(result)); /* 1S */ if (ret < 0) goto out; ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, - sizeof(ordinal)); + sizeof(ordinal)); /* 2S */ if (ret < 0) goto out; va_start(argp, keylen); for (;;) { dlen = va_arg(argp, unsigned int); - if (dlen == 0) - break; dpos = va_arg(argp, unsigned int); + if (!dlen && !dpos) + break; ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); if (ret < 0) break; @@ -263,12 +265,13 @@ static int TSS_checkhmac1(unsigned char *buffer, if (ret < 0) goto out; + /* Generate the HMAC digest */ ret = TSS_rawhmac(testhmac, key, keylen, - SHA1_DIGEST_SIZE, paramdigest, - TPM_NONCE_SIZE, enonce->data, - TPM_NONCE_SIZE, ononce->data, - 1, continueflag, - 0, 0); + /* 1H1 */ SHA1_DIGEST_SIZE, paramdigest, + /* 2H1 */ TPM_NONCE_SIZE, enonce->data, + /* 3H1 */ TPM_NONCE_SIZE, ononce->data, + /* 4H1 */ 1, continueflag, + 0, NULL); if (ret < 0) goto out; @@ -279,7 +282,18 @@ out: return ret; } -/* +/** + * TSS_checkhmac2 - Verify the result of an AUTH2_COMMAND (eg. Unseal) + * @digest: Reply buffer + * @ordinal: The command ID, BE form + * @ononce: Odd nonce + * @key1: The key to use in the authorisation session HMAC generation (nH1) + * @keylen1: The size of @key1 + * @key2: The key to use in the data session HMAC generation (nH2) + * @keylen2: The size of @key2 + * @...: Pairs of size and pointer of data elements to load into hash + * @0,NULL: Terminator + * * verify the AUTH2_COMMAND (unseal) result from TPM */ static int TSS_checkhmac2(const unsigned char *buffer, @@ -323,6 +337,9 @@ static int TSS_checkhmac2(const unsigned char *buffer, enonce1 = (const void *)continueflag1 - TPM_NONCE_SIZE; enonce2 = (const void *)continueflag2 - TPM_NONCE_SIZE; + /* Load the 1S, 2S, 3S, ... marked fields into a hash. The digest + * value is then 1H1 loaded into the HMAC below. + */ sdesc = tpm_init_sdesc(tpm_hashalg); if (IS_ERR(sdesc)) { pr_info("Can't alloc %s\n", tpm_hash_alg); @@ -332,20 +349,20 @@ static int TSS_checkhmac2(const unsigned char *buffer, if (ret < 0) goto out; ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, - sizeof(result)); + sizeof(result)); /* 1S */ if (ret < 0) goto out; ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, - sizeof(ordinal)); + sizeof(ordinal)); /* 2S */ if (ret < 0) goto out; va_start(argp, keylen2); for (;;) { dlen = va_arg(argp, unsigned int); - if (dlen == 0) - break; dpos = va_arg(argp, unsigned int); + if (!dlen && !dpos) + break; ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); if (ret < 0) break; @@ -357,11 +374,11 @@ static int TSS_checkhmac2(const unsigned char *buffer, goto out; ret = TSS_rawhmac(testhmac1, key1, keylen1, - SHA1_DIGEST_SIZE, paramdigest, - TPM_NONCE_SIZE, enonce1->data, - TPM_NONCE_SIZE, ononce->data, - 1, continueflag1, - 0, 0); + /* 1H1 */ SHA1_DIGEST_SIZE, paramdigest, + /* 2H1 */ TPM_NONCE_SIZE, enonce1->data, + /* 3H1 */ TPM_NONCE_SIZE, ononce->data, + /* 4H1 */ 1, continueflag1, + 0, NULL); if (ret < 0) goto out; if (memcmp(testhmac1, authdata1, SHA1_DIGEST_SIZE)) { @@ -369,11 +386,11 @@ static int TSS_checkhmac2(const unsigned char *buffer, goto out; } ret = TSS_rawhmac(testhmac2, key2, keylen2, - SHA1_DIGEST_SIZE, paramdigest, - TPM_NONCE_SIZE, enonce2->data, - TPM_NONCE_SIZE, ononce->data, - 1, continueflag2, - 0, 0); + /* 1H2 */ SHA1_DIGEST_SIZE, paramdigest, + /* 2H2 */ TPM_NONCE_SIZE, enonce2->data, + /* 3H2 */ TPM_NONCE_SIZE, ononce->data, + /* 4H2 */ 1, continueflag2, + 0, NULL); if (ret < 0) goto out; if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE)) @@ -438,7 +455,7 @@ static int tpm_create_osap(struct tpm_chip *chip, return TSS_rawhmac(s->secret, keyauth, SHA1_DIGEST_SIZE, TPM_NONCE_SIZE, enonce.data, TPM_NONCE_SIZE, ononce.data, - 0, 0); + 0, NULL); } /* @@ -484,7 +501,7 @@ struct tpm_digests { * @encbuffer: Buffer to hold the encrypted data (max SHA1_DIGEST_SIZE) * @_enclen: Where to place the size of the encrypted data * @encauth: 'Password' to use to encrypt authorisation key - * @pcrinfo: Information on PCR register values to seal to + * @pcrinfo: Information on PCR register values to seal to (must not be NULL) * @pcrinfosize: size of @pcrinfo * * Have the TPM seal (encrypt) the data in the data buffer. The encryption is @@ -544,28 +561,16 @@ int tpm_seal(struct tpm_chip *chip, td->encauth[i] = td->xorhash[i] ^ encauth[i]; /* calculate authorization HMAC value */ - if (pcrinfosize == 0) { - /* no pcr info specified */ - ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, - &sess.enonce, &td->ononce, cont, - sizeof(__be32), &ordinal_be, - SHA1_DIGEST_SIZE, td->encauth, - sizeof(__be32), &pcrinfosize_be, - sizeof(__be32), &rawlen_be, - rawlen, rawdata, - 0, 0); - } else { - /* pcr info specified */ - ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, - &sess.enonce, &td->ononce, cont, - sizeof(__be32), &ordinal_be, - SHA1_DIGEST_SIZE, td->encauth, - sizeof(__be32), &pcrinfosize_be, - pcrinfosize, pcrinfo, - sizeof(__be32), &rawlen_be, - rawlen, rawdata, - 0, 0); - } + BUG_ON(!pcrinfo); + ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, + &sess.enonce, &td->ononce, cont, + /* 1S */ sizeof(__be32), &ordinal_be, + /* 2S */ SHA1_DIGEST_SIZE, td->encauth, + /* 3S */ sizeof(__be32), &pcrinfosize_be, + /* 4S */ pcrinfosize, pcrinfo, + /* 5S */ sizeof(__be32), &rawlen_be, + /* 6S */ rawlen, rawdata, + 0, NULL); if (ret < 0) goto out; @@ -600,8 +605,8 @@ int tpm_seal(struct tpm_chip *chip, /* check the HMAC in the response */ ret = TSS_checkhmac1(tb->data, ordinal_be, &td->ononce, sess.secret, SHA1_DIGEST_SIZE, - storedsize, TPM_DATA_OFFSET, - 0, 0); + /* 3S */ storedsize, TPM_DATA_OFFSET, + 0, NULL); /* copy the encrypted data to caller's buffer */ if (!ret) { @@ -663,16 +668,16 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, return ret; ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, &enonce1, &ononce, cont, - sizeof(__be32), &ordinal, - enclen, encdata, - 0, 0); + /* 1S */ sizeof(__be32), &ordinal, + /* 2S */ enclen, encdata, + 0, NULL); if (ret < 0) return ret; ret = TSS_authhmac(authdata2, decauth, TPM_NONCE_SIZE, &enonce2, &ononce, cont, - sizeof(__be32), &ordinal, - enclen, encdata, - 0, 0); + /* 1S */ sizeof(__be32), &ordinal, + /* 2S */ enclen, encdata, + 0, NULL); if (ret < 0) return ret; @@ -703,8 +708,8 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, ret = TSS_checkhmac2(tb->data, ordinal, &ononce, keyauth, SHA1_DIGEST_SIZE, decauth, SHA1_DIGEST_SIZE, - sizeof(uint32_t), TPM_DATA_OFFSET, - *_rawlen, TPM_DATA_OFFSET + sizeof(uint32_t), + /* 3S */ sizeof(uint32_t), TPM_DATA_OFFSET, + /* 4S */ *_rawlen, TPM_DATA_OFFSET + sizeof(uint32_t), 0, 0); if (ret < 0) { pr_info("TSS_checkhmac2 failed (%d)\n", ret); |