From patchwork Fri Mar 3 15:19:03 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 735135 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.sourceforge.net (lists.sourceforge.net [216.34.181.88]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3vZXsk67g2z9s1y for ; Sat, 4 Mar 2017 02:19:38 +1100 (AEDT) Received: from localhost ([127.0.0.1] helo=sfs-ml-3.v29.ch3.sourceforge.com) by sfs-ml-3.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1cjozV-00021D-Vs; Fri, 03 Mar 2017 15:19:33 +0000 Received: from sog-mx-2.v43.ch3.sourceforge.com ([172.29.43.192] helo=mx.sourceforge.net) by sfs-ml-3.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1cjozV-000218-A3 for tpmdd-devel@lists.sourceforge.net; Fri, 03 Mar 2017 15:19:33 +0000 X-ACL-Warn: Received: from mga14.intel.com ([192.55.52.115]) by sog-mx-2.v43.ch3.sourceforge.com with esmtps (TLSv1:AES256-SHA:256) (Exim 4.76) id 1cjozU-0000cg-3x for tpmdd-devel@lists.sourceforge.net; Fri, 03 Mar 2017 15:19:33 +0000 Received: from fmsmga006.fm.intel.com ([10.253.24.20]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 03 Mar 2017 07:19:31 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.35,237,1484035200"; d="scan'208";a="72421928" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.60]) by fmsmga006.fm.intel.com with ESMTP; 03 Mar 2017 07:19:29 -0800 From: Jarkko Sakkinen To: tpmdd-devel@lists.sourceforge.net Date: Fri, 3 Mar 2017 17:19:03 +0200 Message-Id: <20170303151912.14752-3-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20170303151912.14752-1-jarkko.sakkinen@linux.intel.com> References: <20170303151912.14752-1-jarkko.sakkinen@linux.intel.com> X-Spam-Score: -0.0 (/) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -0.0 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain X-Headers-End: 1cjozU-0000cg-3x Cc: dhowells@redhat.com, open list , James.Bottomley@HansenPartnership.com, linux-security-module@vger.kernel.org Subject: [tpmdd-devel] [PATCH v3 2/7] tpm: validate TPM 2.0 commands X-BeenThere: tpmdd-devel@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list List-Id: Tpm Device Driver maintainance List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: tpmdd-devel-bounces@lists.sourceforge.net Check for every TPM 2.0 command that the command code is supported and the command buffer has at least the length that can contain the header and the handle area. For ContextSave and FlushContext we mark the body to be part of the handle area. This gives validation for these commands at zero cost, including the body of the command. The more important reason for this is that we can virtualize these commands in the same way as you would virtualize the handle area of a command. Signed-off-by: Jarkko Sakkinen Tested-by: James Bottomley Reviewed-by: James Bottomley --- drivers/char/tpm/tpm-interface.c | 38 +++++++++++++++++- drivers/char/tpm/tpm.h | 15 +++++++ drivers/char/tpm/tpm2-cmd.c | 84 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 132 insertions(+), 5 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 708d356..20b1fe3 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -328,6 +328,42 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); +static bool tpm_validate_command(struct tpm_chip *chip, const u8 *cmd, + size_t len) +{ + const struct tpm_input_header *header = (const void *)cmd; + int i; + u32 cc; + u32 attrs; + unsigned int nr_handles; + + if (len < TPM_HEADER_SIZE) + return false; + + if (chip->flags & TPM_CHIP_FLAG_TPM2 && chip->nr_commands) { + cc = be32_to_cpu(header->ordinal); + + i = tpm2_find_cc(chip, cc); + if (i < 0) { + dev_dbg(&chip->dev, "0x%04X is an invalid command\n", + cc); + return false; + } + + attrs = chip->cc_attrs_tbl[i]; + nr_handles = + 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0)); + if (len < TPM_HEADER_SIZE + 4 * nr_handles) + goto err_len; + } + + return true; +err_len: + dev_dbg(&chip->dev, + "%s: insufficient command length %zu", __func__, len); + return false; +} + /** * tmp_transmit - Internal kernel interface to transmit TPM commands. * @@ -348,7 +384,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, u32 count, ordinal; unsigned long stop; - if (bufsiz < TPM_HEADER_SIZE) + if (!tpm_validate_command(chip, buf, bufsiz)) return -EINVAL; if (bufsiz > TPM_BUFSIZE) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 4937b56..8568dda 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -114,6 +114,7 @@ enum tpm2_command_codes { TPM2_CC_CREATE = 0x0153, TPM2_CC_LOAD = 0x0157, TPM2_CC_UNSEAL = 0x015E, + TPM2_CC_CONTEXT_SAVE = 0x0162, TPM2_CC_FLUSH_CONTEXT = 0x0165, TPM2_CC_GET_CAPABILITY = 0x017A, TPM2_CC_GET_RANDOM = 0x017B, @@ -127,15 +128,25 @@ enum tpm2_permanent_handles { }; enum tpm2_capabilities { + TPM2_CAP_COMMANDS = 2, TPM2_CAP_PCRS = 5, TPM2_CAP_TPM_PROPERTIES = 6, }; +enum tpm2_properties { + TPM_PT_TOTAL_COMMANDS = 0x0129, +}; + enum tpm2_startup_types { TPM2_SU_CLEAR = 0x0000, TPM2_SU_STATE = 0x0001, }; +enum tpm2_cc_attrs { + TPM2_CC_ATTR_CHANDLES = 25, + TPM2_CC_ATTR_RHANDLE = 28, +}; + #define TPM_VID_INTEL 0x8086 #define TPM_VID_WINBOND 0x1050 #define TPM_VID_STM 0x104A @@ -199,6 +210,9 @@ struct tpm_chip { acpi_handle acpi_dev_handle; char ppi_version[TPM_PPI_VERSION_LEN + 1]; #endif /* CONFIG_ACPI */ + + u32 nr_commands; + u32 *cc_attrs_tbl; }; #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) @@ -554,4 +568,5 @@ int tpm2_auto_startup(struct tpm_chip *chip); void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type); unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal); int tpm2_probe(struct tpm_chip *chip); +int tpm2_find_cc(struct tpm_chip *chip, u32 cc); #endif diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 881aea9..5efe9c4 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -1067,15 +1067,76 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) return rc; } +static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip) +{ + struct tpm_buf buf; + u32 nr_commands; + u32 *attrs; + u32 cc; + int i; + int rc; + + rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL); + if (rc) + goto out; + + if (nr_commands > 0xFFFFF) { + rc = -EFAULT; + goto out; + } + + chip->cc_attrs_tbl = devm_kzalloc(&chip->dev, 4 * nr_commands, + GFP_KERNEL); + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY); + if (rc) + goto out; + + tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS); + tpm_buf_append_u32(&buf, TPM2_CC_FIRST); + tpm_buf_append_u32(&buf, nr_commands); + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 9 + 4 * nr_commands, + 0, NULL); + if (rc) { + tpm_buf_destroy(&buf); + goto out; + } + + if (nr_commands != + be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) { + tpm_buf_destroy(&buf); + goto out; + } + + chip->nr_commands = nr_commands; + + attrs = (u32 *)&buf.data[TPM_HEADER_SIZE + 9]; + for (i = 0; i < nr_commands; i++, attrs++) { + chip->cc_attrs_tbl[i] = be32_to_cpup(attrs); + cc = chip->cc_attrs_tbl[i] & 0xFFFF; + + if (cc == TPM2_CC_CONTEXT_SAVE || cc == TPM2_CC_FLUSH_CONTEXT) { + chip->cc_attrs_tbl[i] &= + ~(GENMASK(2, 0) << TPM2_CC_ATTR_CHANDLES); + chip->cc_attrs_tbl[i] |= 1 << TPM2_CC_ATTR_CHANDLES; + } + } + + tpm_buf_destroy(&buf); + +out: + if (rc > 0) + rc = -ENODEV; + return rc; +} + /** * tpm2_auto_startup - Perform the standard automatic TPM initialization * sequence * @chip: TPM chip to use * - * Initializes timeout values for operation and command durations, conducts - * a self-test and reads the list of active PCR banks. - * - * Return: 0 on success. Otherwise, a system error code is returned. + * Returns 0 on success, < 0 in case of fatal error. */ int tpm2_auto_startup(struct tpm_chip *chip) { @@ -1104,9 +1165,24 @@ int tpm2_auto_startup(struct tpm_chip *chip) } rc = tpm2_get_pcr_allocation(chip); + if (rc) + goto out; + + rc = tpm2_get_cc_attrs_tbl(chip); out: if (rc > 0) rc = -ENODEV; return rc; } + +int tpm2_find_cc(struct tpm_chip *chip, u32 cc) +{ + int i; + + for (i = 0; i < chip->nr_commands; i++) + if (cc == (chip->cc_attrs_tbl[i] & GENMASK(15, 0))) + return i; + + return -1; +}