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: 9524085 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 261A2601B7 for ; Wed, 18 Jan 2017 15:11:26 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 17F4328529 for ; Wed, 18 Jan 2017 15:11:26 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0B47E285ED; Wed, 18 Jan 2017 15:11:26 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 65EDD28529 for ; Wed, 18 Jan 2017 15:11:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751571AbdARPKn (ORCPT ); Wed, 18 Jan 2017 10:10:43 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:44642 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751379AbdARPKl (ORCPT ); Wed, 18 Jan 2017 10:10:41 -0500 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) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1484752189; bh=3dxhU0LZj/Vhk8sHYmibj5H+XtMyF6xFMa38LaI4J9s=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=QjJZX/2xWxKF27iKYOHuE9np/MHTz68J0z52z0rv5Kodw4hptGPLddFVngLn0Nl4a fg3vcPH7cHhkOVTq+/rxOsj1m8IHvnqgNx/l2djomiQWzH0J+70mFtu5PdbGlNJ9vX zgcxi17If0gRbPx/Ago6rpYTiC3j2L5wkxzfOVS8= Message-ID: <1484752186.2717.16.camel@HansenPartnership.com> Subject: [PATCH 1/2] tpm2: add session handle isolation to tpm spaces From: James Bottomley To: tpmdd-devel@lists.sourceforge.net Cc: open list , linux-security-module@vger.kernel.org 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 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP 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);