From patchwork Wed Jan 18 15:09:46 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 716693 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 3v3Vkv3p2Sz9snk for ; Thu, 19 Jan 2017 02:09:59 +1100 (AEDT) Received: from localhost ([127.0.0.1] helo=sfs-ml-1.v29.ch3.sourceforge.com) by sfs-ml-1.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1cTrs5-0005qt-Np; Wed, 18 Jan 2017 15:09:57 +0000 Received: from sog-mx-3.v43.ch3.sourceforge.com ([172.29.43.193] helo=mx.sourceforge.net) by sfs-ml-1.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1cTrs4-0005qX-HB for tpmdd-devel@lists.sourceforge.net; Wed, 18 Jan 2017 15:09:56 +0000 Received-SPF: pass (sog-mx-3.v43.ch3.sourceforge.com: domain of HansenPartnership.com designates 66.63.167.143 as permitted sender) client-ip=66.63.167.143; envelope-from=James.Bottomley@HansenPartnership.com; helo=bedivere.hansenpartnership.com; Received: from bedivere.hansenpartnership.com ([66.63.167.143]) by sog-mx-3.v43.ch3.sourceforge.com with esmtps (TLSv1:AES256-SHA:256) (Exim 4.76) id 1cTrs3-0000Al-AB for tpmdd-devel@lists.sourceforge.net; Wed, 18 Jan 2017 15:09:56 +0000 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id 654A48EE2B2; Wed, 18 Jan 2017 07:09:49 -0800 (PST) Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id syxynvLFqBsS; Wed, 18 Jan 2017 07:09:49 -0800 (PST) Received: from [9.232.160.153] (unknown [129.33.253.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by bedivere.hansenpartnership.com (Postfix) with ESMTPSA id AF64C8EE07D; Wed, 18 Jan 2017 07:09:48 -0800 (PST) Message-ID: <1484752186.2717.16.camel@HansenPartnership.com> From: James Bottomley To: tpmdd-devel@lists.sourceforge.net Date: Wed, 18 Jan 2017 10:09:46 -0500 In-Reply-To: <1484752097.2717.14.camel@HansenPartnership.com> References: <1484752097.2717.14.camel@HansenPartnership.com> X-Mailer: Evolution 3.16.5 Mime-Version: 1.0 X-Spam-Score: -4.8 (----) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -1.5 SPF_CHECK_PASS SPF reports sender host as permitted sender for sender-domain -0.0 SPF_PASS SPF: sender matches SPF record -3.2 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-Headers-End: 1cTrs3-0000Al-AB Cc: linux-security-module@vger.kernel.org, open list Subject: [tpmdd-devel] [PATCH 1/2] tpm2: add session handle isolation to tpm spaces 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: , Errors-To: tpmdd-devel-bounces@lists.sourceforge.net sessions should be isolated during each instance of a tpm space. This means that spaces shouldn't be able to see each other's sessions and also when a space is closed, all the sessions belonging to it should be flushed. This is implemented by adding a session_tbl to the space to track the created session handles. Sessions can be flushed either by not setting the continueSession attribute in the session table or by an explicit flush. In the first case we have to mark the session as being ready to flush and explicitly forget it if the command completes successfully and in the second case we have to intercept the flush instruction and clear the session from our table. Finally, when the device handling the space is closed, we have to send explicit flushes to all the remaining sessions belonging to the space to ensure they are cleared out. Signed-off-by: James Bottomley --- drivers/char/tpm/tpm.h | 2 + drivers/char/tpm/tpm2-space.c | 178 ++++++++++++++++++++++++++++++++++++++++-- drivers/char/tpm/tpms-dev.c | 1 + 3 files changed, 173 insertions(+), 8 deletions(-) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 3346c48..265b7f5 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -158,6 +158,7 @@ enum tpm2_cc_attrs { struct tpm_space { u32 context_tbl[14]; u8 *context_buf; + u32 session_tbl[6]; }; enum tpm_chip_flags { @@ -584,4 +585,5 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, u8 *buf, size_t bufsiz); int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, u8 *buf, size_t bufsiz); +void tpm2_flush_space(struct tpm_chip *chip, struct tpm_space *space); #endif diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 3708e70..49048af 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -25,15 +25,83 @@ enum tpm2_handle_types { TPM2_HT_TRANSIENT = 0x80000000, }; -static void tpm2_flush_space(struct tpm_chip *chip) +#define TPM2_HT_TAG_FOR_FLUSH 0xF0000000 + +static int tpm2_session_find(struct tpm_space *space, u32 handle) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) + if (handle == space->session_tbl[i]) + break; + if (i == ARRAY_SIZE(space->session_tbl)) + return -1; + return i; +} + +static int tpm2_session_add(struct tpm_chip *chip, + struct tpm_space *space, u32 handle) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) + if (space->session_tbl[i] == 0) + break; + if (i == ARRAY_SIZE(space->session_tbl)) { + dev_err(&chip->dev, "out of session slots\n"); + tpm2_flush_context_cmd(chip, handle, TPM_TRANSMIT_UNLOCKED); + return -ENOMEM; + } + + space->session_tbl[i] = handle; + + return 0; +} + +/* if a space is active, emulate some commands */ +static int tpm2_intercept(struct tpm_chip *chip, struct tpm_space *space, + u32 cc, u8 *buf, size_t bufsiz) +{ + int j; + u32 handle, handle_type; + + if (!space) + return 0; + + if (cc != TPM2_CC_FLUSH_CONTEXT) + return 0; + handle = get_unaligned_be32((__be32 *)&buf[10]); + handle_type = (handle & 0xFF000000); + + if (handle_type != TPM2_HT_HMAC_SESSION && + handle_type != TPM2_HT_POLICY_SESSION) + /* let the TPM figure out and return the error */ + return 0; + + j = tpm2_session_find(space, handle); + if (j < 0) + return -EINVAL; + + space->session_tbl[j] |= TPM2_HT_TAG_FOR_FLUSH; + + return 0; +} + +void tpm2_flush_space(struct tpm_chip *chip, struct tpm_space *space) { - struct tpm_space *space = &chip->work_space; int i; for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) if (space->context_tbl[i] && ~space->context_tbl[i]) tpm2_flush_context_cmd(chip, space->context_tbl[i], TPM_TRANSMIT_UNLOCKED); + + for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) { + space->session_tbl[i] &= ~TPM2_HT_TAG_FOR_FLUSH; + if (space->session_tbl[i]) + tpm2_flush_context_cmd(chip, space->session_tbl[i], + TPM_TRANSMIT_UNLOCKED); + } } struct tpm2_context { @@ -94,10 +162,82 @@ static int tpm2_load_space(struct tpm_chip *chip) out_err: tpm_buf_destroy(&buf); - tpm2_flush_space(chip); + tpm2_flush_space(chip, space); return rc; } +static void tpm2_unmap_sessions(struct tpm_chip *chip, u32 rc) +{ + struct tpm_space *space = &chip->work_space; + int i; + + for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) { + if ((space->session_tbl[i] & TPM2_HT_TAG_FOR_FLUSH) != + TPM2_HT_TAG_FOR_FLUSH) + continue; + if (rc == TPM2_RC_SUCCESS) + space->session_tbl[i] = 0; + else + /* for unsuccessful command, keep session */ + space->session_tbl[i] &= ~TPM2_HT_TAG_FOR_FLUSH; + } +} + +static int tpm2_map_sessions(struct tpm_chip *chip, u8 *buf, size_t len, + size_t start) +{ + struct tpm_space *space = &chip->work_space; + u32 size = be32_to_cpup((__be32 *)&buf[start]); + int i; + + /* skip over authorizationSize */ + start += 4; + + if (size > len - start) { + dev_err(&chip->dev, "Invalid authorization header size %u\n", + size); + return -EINVAL; + } + + for (i = start; i < start+size; ) { + u16 skip; + u8 attr; + int j; + u32 handle, handle_type; + + /* TPMI_SH_AUTH_SESSION */ + handle = get_unaligned_be32((__be32 *)&buf[i]); + handle_type = handle & 0xFF000000; + i += 4; + /* TPM2B_DIGEST */ + skip = get_unaligned_be16((__be16 *)&buf[i]); + i += skip + sizeof(skip); + /* TPMA_SESSION */ + attr = buf[i++]; + /* TPM2B_AUTH */ + skip = get_unaligned_be16((__be16 *)&buf[i]); + i += skip + sizeof(skip); + + if (handle_type != TPM2_HT_HMAC_SESSION && + handle_type != TPM2_HT_POLICY_SESSION) + continue; + + j = tpm2_session_find(space, handle); + if (j < 0) + return -EINVAL; + if ((attr & 1) == 0) + /* session is flushed by the command */ + space->session_tbl[j] |= TPM2_HT_TAG_FOR_FLUSH; + } + + if (i != start+size) { + dev_err(&chip->dev, "Authorization session overflow\n"); + return -EINVAL; + } + + return 0; +} + static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd, size_t len) { struct tpm_space *space = &chip->work_space; @@ -105,6 +245,7 @@ static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd, size_t len) u32 vhandle; u32 phandle; u32 attrs; + u16 tag = get_unaligned_be16((__be16 *)cmd); int i; int j; int rc; @@ -132,11 +273,14 @@ static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd, size_t len) *((__be32 *)&cmd[TPM_HEADER_SIZE + 4 * i]) = cpu_to_be32(phandle); } + if (tag == TPM2_ST_SESSIONS) + tpm2_map_sessions(chip, cmd, len, + TPM_HEADER_SIZE + 4*nr_handles); return 0; out_err: - tpm2_flush_space(chip); + tpm2_flush_space(chip, space); return rc; } @@ -150,8 +294,14 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, memcpy(&chip->work_space.context_tbl, &space->context_tbl, sizeof(space->context_tbl)); + memcpy(&chip->work_space.session_tbl, &space->session_tbl, + sizeof(space->session_tbl)); memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE); + rc = tpm2_intercept(chip, space, cc, buf, bufsiz); + if (rc) + return rc; + rc = tpm2_load_space(chip); if (rc) return rc; @@ -166,13 +316,17 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, static int tpm2_map_response(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len) { struct tpm_space *space = &chip->work_space; - u32 phandle; + u32 phandle, phandle_type; u32 vhandle; u32 attrs; u32 return_code = get_unaligned_be32((__be32 *)&rsp[6]); + u16 tag = get_unaligned_be16((__be16 *)rsp); int i; int rc; + if (tag == TPM2_ST_SESSIONS) + tpm2_unmap_sessions(chip, return_code); + if (return_code != TPM2_RC_SUCCESS) return 0; @@ -188,9 +342,15 @@ static int tpm2_map_response(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len) return 0; phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]); - if ((phandle & 0xFF000000) != TPM2_HT_TRANSIENT) + phandle_type = (phandle & 0xFF000000); + if (phandle_type != TPM2_HT_TRANSIENT && + phandle_type != TPM2_HT_HMAC_SESSION && + phandle_type != TPM2_HT_POLICY_SESSION) return 0; + if (phandle_type != TPM2_HT_TRANSIENT) + return tpm2_session_add(chip, space, phandle); + /* Garbage collect a dead context. */ for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) { if (space->context_tbl[i] == phandle) { @@ -217,7 +377,7 @@ static int tpm2_map_response(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len) return 0; out_err: - tpm2_flush_space(chip); + tpm2_flush_space(chip, space); return rc; } @@ -277,7 +437,7 @@ static int tpm2_save_space(struct tpm_chip *chip) return 0; out_err: tpm_buf_destroy(&buf); - tpm2_flush_space(chip); + tpm2_flush_space(chip, space); return rc; } @@ -299,6 +459,8 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, memcpy(&space->context_tbl, &chip->work_space.context_tbl, sizeof(space->context_tbl)); + memcpy(&space->session_tbl, &chip->work_space.session_tbl, + sizeof(space->session_tbl)); memcpy(space->context_buf, chip->work_space.context_buf, PAGE_SIZE); return 0; diff --git a/drivers/char/tpm/tpms-dev.c b/drivers/char/tpm/tpms-dev.c index 6bb687f..d6e3491 100644 --- a/drivers/char/tpm/tpms-dev.c +++ b/drivers/char/tpm/tpms-dev.c @@ -36,6 +36,7 @@ static int tpms_release(struct inode *inode, struct file *file) struct file_priv *fpriv = file->private_data; struct tpms_priv *priv = container_of(fpriv, struct tpms_priv, priv); + tpm2_flush_space(fpriv->chip, &priv->space); tpm_common_release(file, fpriv); kfree(priv->space.context_buf); kfree(priv);