From patchwork Fri Jan 13 19:24:13 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 9516289 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 F28C36077E for ; Fri, 13 Jan 2017 19:24:22 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DBFD628766 for ; Fri, 13 Jan 2017 19:24:22 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D0D6C2876D; Fri, 13 Jan 2017 19:24:22 +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.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham 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 CFADB28766 for ; Fri, 13 Jan 2017 19:24:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1750978AbdAMTYV (ORCPT ); Fri, 13 Jan 2017 14:24:21 -0500 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:41808 "EHLO mx0a-001b2d01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750965AbdAMTYU (ORCPT ); Fri, 13 Jan 2017 14:24:20 -0500 Received: from pps.filterd (m0098404.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.17/8.16.0.17) with SMTP id v0DJO4S8138408 for ; Fri, 13 Jan 2017 14:24:19 -0500 Received: from e17.ny.us.ibm.com (e17.ny.us.ibm.com [129.33.205.207]) by mx0a-001b2d01.pphosted.com with ESMTP id 27y28e68c5-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Fri, 13 Jan 2017 14:24:19 -0500 Received: from localhost by e17.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Fri, 13 Jan 2017 14:24:18 -0500 Received: from d01dlp03.pok.ibm.com (9.56.250.168) by e17.ny.us.ibm.com (146.89.104.204) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Fri, 13 Jan 2017 14:24:16 -0500 Received: from b01cxnp22035.gho.pok.ibm.com (b01cxnp22035.gho.pok.ibm.com [9.57.198.25]) by d01dlp03.pok.ibm.com (Postfix) with ESMTP id 74A68C9003E; Fri, 13 Jan 2017 14:23:58 -0500 (EST) Received: from b01ledav005.gho.pok.ibm.com (b01ledav005.gho.pok.ibm.com [9.57.199.110]) by b01cxnp22035.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v0DJOF7u43319332; Fri, 13 Jan 2017 19:24:15 GMT Received: from b01ledav005.gho.pok.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id C560AAE051; Fri, 13 Jan 2017 14:24:15 -0500 (EST) Received: from jarvis.ext.hansenpartnership.com (unknown [9.80.199.178]) by b01ledav005.gho.pok.ibm.com (Postfix) with ESMTPS id D7B5DAE03C; Fri, 13 Jan 2017 14:24:14 -0500 (EST) Subject: [PATCH] tpm: add session handles to the save and restore of the tpm2 space manager From: James Bottomley To: linux-security-module@vger.kernel.org, tpmdd-devel@lists.sourceforge.net Cc: open list , Jarkko Sakkinen Date: Fri, 13 Jan 2017 11:24:13 -0800 X-Mailer: Evolution 3.16.5 Mime-Version: 1.0 X-TM-AS-GCONF: 00 X-Content-Scanned: Fidelis XPS MAILER x-cbid: 17011319-0040-0000-0000-0000025A930D X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00006427; HX=3.00000240; KW=3.00000007; PH=3.00000004; SC=3.00000199; SDB=6.00807225; UDB=6.00392915; IPR=6.00584556; BA=6.00005053; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00013912; XFM=3.00000011; UTC=2017-01-13 19:24:18 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17011319-0041-0000-0000-0000064DA26D Message-Id: <1484335453.2527.31.camel@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-01-13_12:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=0 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1612050000 definitions=main-1701130257 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Session handles are slightly more difficult to manage because any TPM only has a finite number of allowed handles, even if the session has been saved; so when you context save a session, you must not flush it because that would destroy the ability to context load it (you only flush sessions when you're done with them) and the tpm won't re-use the handle. Additionally, sessions can be flushed as part of the successful execution of a command if the continueSession attribute is clear, so we have to mark any session we find in the command (using TPM2_HT_TAG_FOR_FLUSH) so it can be cleared from the space if the command successfully executes. Finally, a session may also be cleared by flushing it, so we have to emulate the TPM2_FlushContext command to see if a session is being flushed and manually clear it from the space. We also fully flush all sessions on device close. Signed-off-by: James Bottomley --- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index f5c9355..d8e896e 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -400,6 +400,10 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, if (chip->dev.parent) pm_runtime_get_sync(chip->dev.parent); + rc = tpm2_emulate(chip, space, ordinal, buf, bufsiz); + if (rc) + goto out; + rc = tpm2_prepare_space(chip, space, ordinal, buf, bufsiz); if (rc) goto out; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index adf7810..b922652 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -136,7 +136,8 @@ enum tpm2_capabilities { }; enum tpm2_properties { - TPM_PT_TOTAL_COMMANDS = 0x0129, + TPM_PT_ACTIVE_SESSIONS_MAX = 0x0111, + TPM_PT_TOTAL_COMMANDS = 0x0129, }; enum tpm2_startup_types { @@ -214,6 +215,8 @@ struct tpm_chip { struct tpm_space *work_space; u32 nr_commands; u32 *cc_attrs_tbl; + struct tpm_sessions *sessions; + int max_sessions; }; #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) @@ -583,4 +586,7 @@ 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); +int tpm2_emulate(struct tpm_chip *chip, struct tpm_space *space, + u32 cc, u8 *buf, size_t bufsiz); #endif diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 285361e..e8e9f32 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -25,15 +25,29 @@ enum tpm2_handle_types { TPM2_HT_TRANSIENT = 0x80000000, }; -static void tpm2_flush_space(struct tpm_chip *chip) +#define TPM2_HT_TAG_FOR_FLUSH 0xF0000000 + +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->context_tbl); i++) { + u32 handle = space->context_tbl[i]; + u32 handle_type; + + if (handle == 0 || handle == ~0) + continue; + + if ((handle & TPM2_HT_TAG_FOR_FLUSH) == + TPM2_HT_TAG_FOR_FLUSH) + handle &= ~TPM2_HT_TAG_FOR_FLUSH; + + handle_type = (handle & 0xFF000000); + + tpm2_flush_context_cmd(chip, handle, TPM_TRANSMIT_UNLOCKED); + + space->context_tbl[i] = 0; + } } struct tpm2_context { @@ -54,15 +68,11 @@ static int tpm2_load_space(struct tpm_chip *chip) u32 s; for (i = 0, j = 0; i < ARRAY_SIZE(space->context_tbl); i++) { + u32 phandle, phandle_type; + if (!space->context_tbl[i]) continue; - /* sanity check, should never happen */ - if (~space->context_tbl[i]) { - dev_err(&chip->dev, "context table is inconsistent"); - return -EFAULT; - } - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD); if (rc) @@ -80,9 +90,23 @@ static int tpm2_load_space(struct tpm_chip *chip) rc = -EFAULT; goto out_err; } + phandle = get_unaligned_be32((__be32 *)&buf.data[TPM_HEADER_SIZE]); + phandle_type = (phandle & 0xFF000000); + if (phandle_type == TPM2_HT_TRANSIENT && + space->context_tbl[i] != ~0) { + dev_err(&chip->dev, "context table is inconsistent"); + rc = -EFAULT; + goto out_err; + } + if ((phandle_type == TPM2_HT_HMAC_SESSION || + phandle_type == TPM2_HT_POLICY_SESSION) && + space->context_tbl[i] != phandle) { + dev_err(&chip->dev, "session handle changed\n"); + rc = -EFAULT; + goto out_err; + } - space->context_tbl[i] = - be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE]); + space->context_tbl[i] = phandle; j += s; @@ -93,10 +117,85 @@ 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->context_tbl); i++) { + if ((space->context_tbl[i] & TPM2_HT_TAG_FOR_FLUSH) != + TPM2_HT_TAG_FOR_FLUSH) + continue; + if (rc == TPM2_RC_SUCCESS) + space->context_tbl[i] = 0; + else + /* for unsuccessful command, keep session */ + space->context_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; + __be32 *handlep; + u8 attr; + int j; + u32 handle_type; + + /* TPMI_SH_AUTH_SESSION */ + handlep = (__be32 *)&buf[i]; + handle_type = get_unaligned_be32(handlep) & 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 = 0xFFFFFF - (get_unaligned_be32(handlep) & 0xFFFFFF); + if (j > ARRAY_SIZE(space->context_tbl) || + !space->context_tbl[j]) + return -EINVAL; + put_unaligned_be32(space->context_tbl[j], handlep); + if ((attr & 1) == 0) + /* session is flushed by the command */ + space->context_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; @@ -104,6 +203,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; @@ -131,11 +231,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; } @@ -163,13 +266,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; @@ -185,7 +292,10 @@ 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; /* Garbage collect a dead context. */ @@ -208,13 +318,13 @@ static int tpm2_map_response(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len) } space->context_tbl[i] = phandle; - vhandle = TPM2_HT_TRANSIENT | (0xFFFFFF - i); + vhandle = phandle_type | (0xFFFFFF - i); *(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle); return 0; out_err: - tpm2_flush_space(chip); + tpm2_flush_space(chip, space); return rc; } @@ -228,9 +338,20 @@ static int tpm2_save_space(struct tpm_chip *chip) u32 s; for (i = 0, j = 0; i < ARRAY_SIZE(space->context_tbl); i++) { - if (!(space->context_tbl[i] && ~space->context_tbl[i])) + u32 phandle, phandle_type; + + phandle = space->context_tbl[i]; + + if (phandle == 0) continue; + if (phandle == ~0) { + dev_err(&chip->dev, "context table is inconsistent\n"); + return -EFAULT; + } + + phandle_type = (phandle & 0xFF000000); + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE); if (rc) @@ -260,10 +381,11 @@ static int tpm2_save_space(struct tpm_chip *chip) memcpy(&space->context_buf[j], &buf.data[TPM_HEADER_SIZE], s); - tpm2_flush_context_cmd(chip, space->context_tbl[i], - TPM_TRANSMIT_UNLOCKED); - - space->context_tbl[i] = ~0; + if (phandle_type == TPM2_HT_TRANSIENT) { + tpm2_flush_context_cmd(chip, space->context_tbl[i], + TPM_TRANSMIT_UNLOCKED); + space->context_tbl[i] = ~0; + } j += s; @@ -273,7 +395,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; } @@ -297,3 +419,60 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, return 0; } + +/* if a space is active, emulate some commands */ +int tpm2_emulate(struct tpm_chip *chip, struct tpm_space *space, + u32 cc, u8 *buf, size_t bufsiz) +{ + int i, j, k; + u32 vhandle, handle_type, phandle; + struct tpm2_context *ctx; + static struct tpm_output_header buf_rc = { + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .length = cpu_to_be32(sizeof(struct tpm_output_header)), + }; + + if (!space) + return 0; + + if (cc != TPM2_CC_FLUSH_CONTEXT) + return 0; + vhandle = get_unaligned_be32((__be32 *)&buf[10]); + handle_type = (vhandle & 0xFF000000); + + if (handle_type != TPM2_HT_HMAC_SESSION && + handle_type != TPM2_HT_POLICY_SESSION && + handle_type != TPM2_HT_TRANSIENT) + /* let the TPM figure out and return the error */ + return 0; + + buf_rc.return_code = TPM2_RC_HANDLE; + + j = 0xFFFFFF - (vhandle & 0xFFFFFF); + if (j > ARRAY_SIZE(space->context_tbl)) + goto error; + phandle = space->context_tbl[j]; + if (phandle != ~0) + goto error; + + for (i = 0, k = 0; i <= j; i++) { + ctx = (struct tpm2_context *)&space->context_buf[k]; + + if (space->context_tbl[i] == 0) + continue; + + k += sizeof(*ctx) + get_unaligned_be16(&ctx->blob_size); + } + /* move all the contexts up */ + memcpy(ctx, &space->context_buf[k], PAGE_SIZE - k); + space->context_tbl[j] = 0; + + if (handle_type != TPM2_HT_TRANSIENT) + tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_UNLOCKED); + + buf_rc.return_code = TPM2_RC_SUCCESS; + + error: + memcpy(buf, &buf_rc, sizeof(buf_rc)); + return sizeof(buf_rc); +} diff --git a/drivers/char/tpm/tpms-dev.c b/drivers/char/tpm/tpms-dev.c index c10b308..3eb5955 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);