Upgrade to 4.4.50-rt62
[kvmfornfv.git] / kernel / drivers / char / tpm / tpm2-cmd.c
index 011909a..cb7e4f6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Intel Corporation
+ * Copyright (C) 2014, 2015 Intel Corporation
  *
  * Authors:
  * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
  */
 
 #include "tpm.h"
+#include <keys/trusted-type.h>
+
+enum tpm2_object_attributes {
+       TPM2_ATTR_USER_WITH_AUTH        = BIT(6),
+};
 
 struct tpm2_startup_in {
        __be16  startup_type;
@@ -259,7 +264,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
               sizeof(cmd.params.pcrread_in.pcr_select));
        cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
 
-       rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+       rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
                              "attempting to read a pcr value");
        if (rc == 0) {
                buf = cmd.params.pcrread_out.digest;
@@ -307,7 +312,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash)
        cmd.params.pcrextend_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1);
        memcpy(cmd.params.pcrextend_in.digest, hash, TPM_DIGEST_SIZE);
 
-       rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+       rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
                              "attempting extend a PCR value");
 
        return rc;
@@ -353,7 +358,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
                cmd.header.in = tpm2_getrandom_header;
                cmd.params.getrandom_in.size = cpu_to_be16(num_bytes);
 
-               err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+               err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
                                       "attempting get random");
                if (err)
                        break;
@@ -380,6 +385,283 @@ static const struct tpm_input_header tpm2_get_tpm_pt_header = {
        .ordinal = cpu_to_be32(TPM2_CC_GET_CAPABILITY)
 };
 
+/**
+ * Append TPMS_AUTH_COMMAND to the buffer. The buffer must be allocated with
+ * tpm_buf_alloc().
+ *
+ * @param buf: an allocated tpm_buf instance
+ * @param nonce: the session nonce, may be NULL if not used
+ * @param nonce_len: the session nonce length, may be 0 if not used
+ * @param attributes: the session attributes
+ * @param hmac: the session HMAC or password, may be NULL if not used
+ * @param hmac_len: the session HMAC or password length, maybe 0 if not used
+ */
+static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
+                                const u8 *nonce, u16 nonce_len,
+                                u8 attributes,
+                                const u8 *hmac, u16 hmac_len)
+{
+       tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len);
+       tpm_buf_append_u32(buf, session_handle);
+       tpm_buf_append_u16(buf, nonce_len);
+
+       if (nonce && nonce_len)
+               tpm_buf_append(buf, nonce, nonce_len);
+
+       tpm_buf_append_u8(buf, attributes);
+       tpm_buf_append_u16(buf, hmac_len);
+
+       if (hmac && hmac_len)
+               tpm_buf_append(buf, hmac, hmac_len);
+}
+
+/**
+ * tpm2_seal_trusted() - seal the payload of a trusted key
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: < 0 on error and 0 on success.
+ */
+int tpm2_seal_trusted(struct tpm_chip *chip,
+                     struct trusted_key_payload *payload,
+                     struct trusted_key_options *options)
+{
+       unsigned int blob_len;
+       struct tpm_buf buf;
+       int rc;
+
+       rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
+       if (rc)
+               return rc;
+
+       tpm_buf_append_u32(&buf, options->keyhandle);
+       tpm2_buf_append_auth(&buf, TPM2_RS_PW,
+                            NULL /* nonce */, 0,
+                            0 /* session_attributes */,
+                            options->keyauth /* hmac */,
+                            TPM_DIGEST_SIZE);
+
+       /* sensitive */
+       tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1);
+
+       tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE);
+       tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE);
+       tpm_buf_append_u16(&buf, payload->key_len + 1);
+       tpm_buf_append(&buf, payload->key, payload->key_len);
+       tpm_buf_append_u8(&buf, payload->migratable);
+
+       /* public */
+       tpm_buf_append_u16(&buf, 14);
+
+       tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH);
+       tpm_buf_append_u16(&buf, TPM2_ALG_SHA256);
+       tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH);
+       tpm_buf_append_u16(&buf, 0); /* policy digest size */
+       tpm_buf_append_u16(&buf, TPM2_ALG_NULL);
+       tpm_buf_append_u16(&buf, 0);
+
+       /* outside info */
+       tpm_buf_append_u16(&buf, 0);
+
+       /* creation PCR */
+       tpm_buf_append_u32(&buf, 0);
+
+       if (buf.flags & TPM_BUF_OVERFLOW) {
+               rc = -E2BIG;
+               goto out;
+       }
+
+       rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, "sealing data");
+       if (rc)
+               goto out;
+
+       blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]);
+       if (blob_len > MAX_BLOB_SIZE) {
+               rc = -E2BIG;
+               goto out;
+       }
+
+       memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len);
+       payload->blob_len = blob_len;
+
+out:
+       tpm_buf_destroy(&buf);
+
+       if (rc > 0)
+               rc = -EPERM;
+
+       return rc;
+}
+
+/**
+ * tpm2_load_cmd() - execute a TPM2_Load command
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: same as with tpm_transmit_cmd
+ */
+static int tpm2_load_cmd(struct tpm_chip *chip,
+                        struct trusted_key_payload *payload,
+                        struct trusted_key_options *options,
+                        u32 *blob_handle, unsigned int flags)
+{
+       struct tpm_buf buf;
+       unsigned int private_len;
+       unsigned int public_len;
+       unsigned int blob_len;
+       int rc;
+
+       private_len = be16_to_cpup((__be16 *) &payload->blob[0]);
+       if (private_len > (payload->blob_len - 2))
+               return -E2BIG;
+
+       public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]);
+       blob_len = private_len + public_len + 4;
+       if (blob_len > payload->blob_len)
+               return -E2BIG;
+
+       rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+       if (rc)
+               return rc;
+
+       tpm_buf_append_u32(&buf, options->keyhandle);
+       tpm2_buf_append_auth(&buf, TPM2_RS_PW,
+                            NULL /* nonce */, 0,
+                            0 /* session_attributes */,
+                            options->keyauth /* hmac */,
+                            TPM_DIGEST_SIZE);
+
+       tpm_buf_append(&buf, payload->blob, blob_len);
+
+       if (buf.flags & TPM_BUF_OVERFLOW) {
+               rc = -E2BIG;
+               goto out;
+       }
+
+       rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "loading blob");
+       if (!rc)
+               *blob_handle = be32_to_cpup(
+                       (__be32 *) &buf.data[TPM_HEADER_SIZE]);
+
+out:
+       tpm_buf_destroy(&buf);
+
+       if (rc > 0)
+               rc = -EPERM;
+
+       return rc;
+}
+
+/**
+ * tpm2_flush_context_cmd() - execute a TPM2_FlushContext command
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: same as with tpm_transmit_cmd
+ */
+static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
+                                  unsigned int flags)
+{
+       struct tpm_buf buf;
+       int rc;
+
+       rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
+       if (rc) {
+               dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n",
+                        handle);
+               return;
+       }
+
+       tpm_buf_append_u32(&buf, handle);
+
+       rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags,
+                             "flushing context");
+       if (rc)
+               dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle,
+                        rc);
+
+       tpm_buf_destroy(&buf);
+}
+
+/**
+ * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: same as with tpm_transmit_cmd
+ */
+static int tpm2_unseal_cmd(struct tpm_chip *chip,
+                          struct trusted_key_payload *payload,
+                          struct trusted_key_options *options,
+                          u32 blob_handle, unsigned int flags)
+{
+       struct tpm_buf buf;
+       u16 data_len;
+       u8 *data;
+       int rc;
+
+       rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+       if (rc)
+               return rc;
+
+       tpm_buf_append_u32(&buf, blob_handle);
+       tpm2_buf_append_auth(&buf, TPM2_RS_PW,
+                            NULL /* nonce */, 0,
+                            0 /* session_attributes */,
+                            options->blobauth /* hmac */,
+                            TPM_DIGEST_SIZE);
+
+       rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "unsealing");
+       if (rc > 0)
+               rc = -EPERM;
+
+       if (!rc) {
+               data_len = be16_to_cpup(
+                       (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
+               data = &buf.data[TPM_HEADER_SIZE + 6];
+
+               memcpy(payload->key, data, data_len - 1);
+               payload->key_len = data_len - 1;
+               payload->migratable = data[data_len - 1];
+       }
+
+       tpm_buf_destroy(&buf);
+       return rc;
+}
+
+/**
+ * tpm_unseal_trusted() - unseal the payload of a trusted key
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: < 0 on error and 0 on success.
+ */
+int tpm2_unseal_trusted(struct tpm_chip *chip,
+                       struct trusted_key_payload *payload,
+                       struct trusted_key_options *options)
+{
+       u32 blob_handle;
+       int rc;
+
+       mutex_lock(&chip->tpm_mutex);
+       rc = tpm2_load_cmd(chip, payload, options, &blob_handle,
+                          TPM_TRANSMIT_UNLOCKED);
+       if (rc)
+               goto out;
+
+       rc = tpm2_unseal_cmd(chip, payload, options, blob_handle,
+                            TPM_TRANSMIT_UNLOCKED);
+       tpm2_flush_context_cmd(chip, blob_handle, TPM_TRANSMIT_UNLOCKED);
+out:
+       mutex_unlock(&chip->tpm_mutex);
+       return rc;
+}
+
 /**
  * tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property
  * @chip:              TPM chip to use.
@@ -402,9 +684,9 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,  u32 *value,
        cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id);
        cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
 
-       rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), desc);
+       rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, desc);
        if (!rc)
-               *value = cmd.params.get_tpm_pt_out.value;
+               *value = be32_to_cpu(cmd.params.get_tpm_pt_out.value);
 
        return rc;
 }
@@ -436,7 +718,7 @@ int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
        cmd.header.in = tpm2_startup_header;
 
        cmd.params.startup_in.startup_type = cpu_to_be16(startup_type);
-       return tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+       return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
                                "attempting to start the TPM");
 }
 EXPORT_SYMBOL_GPL(tpm2_startup);
@@ -465,7 +747,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
        cmd.header.in = tpm2_shutdown_header;
        cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type);
 
-       rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), "stopping the TPM");
+       rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, "stopping the TPM");
 
        /* In places where shutdown command is sent there's no much we can do
         * except print the error code on a system failure.
@@ -531,7 +813,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
        cmd.header.in = tpm2_selftest_header;
        cmd.params.selftest_in.full_test = full;
 
-       rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE,
+       rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, 0,
                              "continue selftest");
 
        /* At least some prototype chips seem to give RC_TESTING error
@@ -583,7 +865,7 @@ int tpm2_do_selftest(struct tpm_chip *chip)
                cmd.params.pcrread_in.pcr_select[1] = 0x00;
                cmd.params.pcrread_in.pcr_select[2] = 0x00;
 
-               rc = tpm_transmit_cmd(chip, (u8 *) &cmd, sizeof(cmd), NULL);
+               rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, NULL);
                if (rc < 0)
                        break;
 
@@ -632,7 +914,7 @@ int tpm2_probe(struct tpm_chip *chip)
        cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(0x100);
        cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
 
-       rc = tpm_transmit(chip, (const char *) &cmd, sizeof(cmd));
+       rc = tpm_transmit(chip, (const u8 *)&cmd, sizeof(cmd), 0);
        if (rc <  0)
                return rc;
        else if (rc < TPM_HEADER_SIZE)