From patchwork Sat Feb 20 01:32:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 12096507 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2FDCEC433E6 for ; Sat, 20 Feb 2021 01:34:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 00A0E64EDE for ; Sat, 20 Feb 2021 01:34:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229884AbhBTBe2 (ORCPT ); Fri, 19 Feb 2021 20:34:28 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44172 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229771AbhBTBe1 (ORCPT ); Fri, 19 Feb 2021 20:34:27 -0500 Received: from mail-qt1-x84a.google.com (mail-qt1-x84a.google.com [IPv6:2607:f8b0:4864:20::84a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AF0D8C061794 for ; Fri, 19 Feb 2021 17:33:09 -0800 (PST) Received: by mail-qt1-x84a.google.com with SMTP id d10so4441963qtx.8 for ; Fri, 19 Feb 2021 17:33:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=CtLRPHa6vVfWJfnUvKFAfqInMZ/2zkbzy/KaLuvq/RY=; b=np6G+qmB02m6CtsdX1J98BauvoCRqwR9uUAoQZ22HnNJ8AErOq7qAe01yv81qnazq7 7bmJqaZaEgG8ceyd+8a5WTsKEgcbxIK2fYrKz41nff+TJH3yWqdUMkWWFx62d5DLgJqZ n5/KdFSRNpxtx0Q+UEJQxrJfHUm3F4XGFEhSRwlZeLop/Ez9xYlluW4mTSJE8FE97q1v cosSPTLjucB4Jk1Cyy3x0f/w0r7Fv4vja9ht+0qyAIOFidMadt/oE6GmN2Wx24XSpQcx 62/kE5HdcY/YJL1Q5kiK5ftf3dCNwHukTsdRn8vzZZHFcU/3+A1jYEvb7iboCMpeH5L7 X+uQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=CtLRPHa6vVfWJfnUvKFAfqInMZ/2zkbzy/KaLuvq/RY=; b=lz8zfc9Wdi3prLa63ZaR/k/OvzL6KzBy1sQXk9cgvO3Jq/byC6/HinPkw8VSmpq+Q9 cohxywG50483vkBsg4u45e1IFd36AP861lLTwH+2i4aNtPrYxIdLg4rDOCajSZnqR92U WW49+bSzh5kTxomev066ZbxSs6S7XucVD1RSoLyxrRtXA0foKp72rpaR7vseFO19K/BF yy/ESGTg0C7MGuUu2j0Za8C5kHICWP6ffzkjhUhpvBT/5IZLbPT/aG7UMDS9WqJdLaHZ ICLjvFSKcXJyjRemL/GkF+vynWwTMvtmfQ8/t4aJB3+22UiywIj9RLcz6pzZhEq0UzVX wT7A== X-Gm-Message-State: AOAM531hzBabLBeQVQf5lgeQXL762fk9aEml3yDlvLvC40sOXPTOwCdV jGl00FvSd8a+qrHaS9RoFTOWP9KkpfBGaKntDK+QWQ== X-Google-Smtp-Source: ABdhPJxgycPtcGyLtpEWUmPPRqEvf5yxoDeFB4QBUiNxo/8m3G60P8mXpFrAXLh1D4foiHWOPPTgCdpjOwOooBtVjqzOIQ== Sender: "matthewgarrett via sendgmr" X-Received: from matthewgarrett-tmp.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:1081]) (user=matthewgarrett job=sendgmr) by 2002:a0c:ac43:: with SMTP id m3mr12016912qvb.37.1613784787904; Fri, 19 Feb 2021 17:33:07 -0800 (PST) Date: Sat, 20 Feb 2021 01:32:47 +0000 In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com> Message-Id: <20210220013255.1083202-2-matthewgarrett@google.com> Mime-Version: 1.0 References: <20210220013255.1083202-1-matthewgarrett@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH 1/9] tpm: Add support for in-kernel resetting of PCRs From: Matthew Garrett To: linux-kernel@vger.kernel.org Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org, keyrings@vger.kernel.org, zohar@linux.ibm.com, jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net, rjw@rjwysocki.net, Matthew Garrett , Matthew Garrett Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Add an internal command for resetting a PCR. Signed-off-by: Matthew Garrett --- drivers/char/tpm/tpm-interface.c | 28 +++++++++++++++++++++++++ drivers/char/tpm/tpm.h | 2 ++ drivers/char/tpm/tpm1-cmd.c | 34 ++++++++++++++++++++++++++++++ drivers/char/tpm/tpm2-cmd.c | 36 ++++++++++++++++++++++++++++++++ include/linux/tpm.h | 7 +++++++ 5 files changed, 107 insertions(+) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 1621ce818705..17b8643ee109 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -342,6 +342,34 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, } EXPORT_SYMBOL_GPL(tpm_pcr_extend); +/** + * tpm_pcr_reset - reset the specified PCR + * @chip: a &struct tpm_chip instance, %NULL for the default chip + * @pcr_idx: the PCR to be reset + * + * Return: same as with tpm_transmit_cmd() + */ +int tpm_pcr_reset(struct tpm_chip *chip, u32 pcr_idx) +{ + int rc; + + chip = tpm_find_get_ops(chip); + if (!chip) + return -ENODEV; + + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + rc = tpm2_pcr_reset(chip, pcr_idx); + goto out; + } + + rc = tpm1_pcr_reset(chip, pcr_idx, "attempting to reset a PCR"); + +out: + tpm_put_ops(chip); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_pcr_reset); + /** * tpm_send - send a TPM command * @chip: a &struct tpm_chip instance, %NULL for the default chip diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 947d1db0a5cc..746f7696bdc0 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -176,6 +176,7 @@ int tpm1_get_timeouts(struct tpm_chip *chip); unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal); int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash, const char *log_msg); +int tpm1_pcr_reset(struct tpm_chip *chip, u32 pcr_idx, const char *log_msg); int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf); ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, const char *desc, size_t min_cap_length); @@ -220,6 +221,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digest, u16 *digest_size_ptr); int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digests); +int tpm2_pcr_reset(struct tpm_chip *chip, u32 pcr_idx); int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc); diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index ca7158fa6e6c..36990e9d2dc1 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -478,6 +478,40 @@ int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash, return rc; } +struct tpm_pcr_selection { + u16 size_of_select; + u8 pcr_select[3]; +} __packed; + +#define TPM_ORD_PCR_RESET 200 +int tpm1_pcr_reset(struct tpm_chip *chip, u32 pcr_idx, const char *log_msg) +{ + struct tpm_pcr_selection selection; + struct tpm_buf buf; + int i, rc; + char tmp; + + rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_RESET); + if (rc) + return rc; + + selection.size_of_select = 3; + + for (i = 0; i < selection.size_of_select; i++) { + tmp = 0; + if (pcr_idx / 3 == i) { + pcr_idx -= i * 8; + tmp |= 1 << pcr_idx; + } + selection.pcr_select[i] = tmp; + } + tpm_buf_append(&buf, (u8 *)&selection, sizeof(selection)); + + rc = tpm_transmit_cmd(chip, &buf, sizeof(selection), log_msg); + tpm_buf_destroy(&buf); + return rc; +} + #define TPM_ORD_GET_CAP 101 ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, const char *desc, size_t min_cap_length) diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index eff1f12d981a..9609ae8086c6 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -269,6 +269,42 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, return rc; } +/** + * tpm2_pcr_reset() - reset a PCR + * + * @chip: TPM chip to use. + * @pcr_idx: index of the PCR. + * + * Return: Same as with tpm_transmit_cmd. + */ +int tpm2_pcr_reset(struct tpm_chip *chip, u32 pcr_idx) +{ + struct tpm_buf buf; + struct tpm2_null_auth_area auth_area; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_RESET); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, pcr_idx); + + auth_area.handle = cpu_to_be32(TPM2_RS_PW); + auth_area.nonce_size = 0; + auth_area.attributes = 0; + auth_area.auth_size = 0; + + tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area)); + tpm_buf_append(&buf, (const unsigned char *)&auth_area, + sizeof(auth_area)); + + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to reset a PCR"); + + tpm_buf_destroy(&buf); + + return rc; +} + struct tpm2_get_random_out { __be16 size; u8 buffer[TPM_MAX_RNG_DATA]; diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 8f4ff39f51e7..e2075e2242a0 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -211,6 +211,7 @@ enum tpm2_command_codes { TPM2_CC_HIERARCHY_CONTROL = 0x0121, TPM2_CC_HIERARCHY_CHANGE_AUTH = 0x0129, TPM2_CC_CREATE_PRIMARY = 0x0131, + TPM2_CC_PCR_RESET = 0x013D, TPM2_CC_SEQUENCE_COMPLETE = 0x013E, TPM2_CC_SELF_TEST = 0x0143, TPM2_CC_STARTUP = 0x0144, @@ -399,6 +400,7 @@ static inline u32 tpm2_rc_value(u32 rc) extern int tpm_is_tpm2(struct tpm_chip *chip); extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digest); +extern int tpm_pcr_reset(struct tpm_chip *chip, u32 pcr_idx); extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digests); extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen); @@ -417,6 +419,11 @@ static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, return -ENODEV; } +static inline int tpm_pcr_reset(struct tpm_chip *chip, int pcr_idx) +{ + return -ENODEV; +} + static inline int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digests) { From patchwork Sat Feb 20 01:32:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 12096511 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4D5DEC433E9 for ; Sat, 20 Feb 2021 01:34:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 280DA64EE9 for ; Sat, 20 Feb 2021 01:34:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229889AbhBTBea (ORCPT ); Fri, 19 Feb 2021 20:34:30 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44180 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229868AbhBTBe2 (ORCPT ); Fri, 19 Feb 2021 20:34:28 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 59AFFC0617A9 for ; Fri, 19 Feb 2021 17:33:10 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id v196so8820602ybv.3 for ; Fri, 19 Feb 2021 17:33:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=y0augfXTryPPckNIjyG37nVNdhig6ZbrGk3G+Se0bhk=; b=RAnSVHS6Smj0l3LTFE/vx6JcAMiDFRCoCBnWUB8vImivgo1DTitkKgcGHqMjwAJw/O txIgPqjTqa5i4y7plNhFKxAOjXZ8FqAPcnC1jxvxhEqDXMk3kcqnC0fP9TLI25gVJUXK ddHY50qffEoya6LEsxV2vjOe6YrzC22XhdLQxxrKho7+T0KrN7kp1rjJhsyKoU/cKfYb TEn1URRMkGXIyknaMegtLk6tmlOfKyVP5pr7+ftAFXeT0hi4EscWeLB1AHcTBFFNiuzt LWSmztlO4C9ylDtqEpai3Jw11CILCmSXimM0qGQkwtnWxlfXul5qzttJBxAyTWmez48Z zDyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=y0augfXTryPPckNIjyG37nVNdhig6ZbrGk3G+Se0bhk=; b=IjxFF0ekjhXYLdXDLRC0mVMGaDrC9Tiu8bjn+qHRGuM+lQfxiuKq+EvWwpqyWnLQi2 Bz8iwqKyL5YahyArmAtfYHejSq1APtkt+6fROUHRYFUCZTlWhF3oZ7dlDpazH041+SqP v0y+5m1gyYXbRqJguAvywR5fishHjNDrHD7rb3jFdhJJIR0L2VJZpsx1bFfMDQem3KKD mNM5FH8i9aV98zJmOE752AjfKSD/rkc13U1ig4XOgQNaMq2w9fJB3hqp12k68fdZ7wJr O0nM8HPqJOaTul2jTMlvbFQ85yQRpGUFuIPHWq9pvBrd+ZqJdV/UjbGP50wY5k9Dw6mA z32g== X-Gm-Message-State: AOAM532ITcrNs5X1HPO6NkL5g9EfkI9kipMcyT1vhxbC1TXaFMw3i76b vM2LgJwjaJU6Hd2fk1esO/L9bW9H37FrIlOy71/ibg== X-Google-Smtp-Source: ABdhPJytRkNN3Yr0iLNpnA3MzM1hsHO7gs7Uv3eNdAOwxj6Y/9Z3hSrHrL2ZuhfAoEO1BriPoncl5gzOqTYtEKVrKpRP2A== Sender: "matthewgarrett via sendgmr" X-Received: from matthewgarrett-tmp.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:1081]) (user=matthewgarrett job=sendgmr) by 2002:a25:9383:: with SMTP id a3mr17879351ybm.215.1613784789563; Fri, 19 Feb 2021 17:33:09 -0800 (PST) Date: Sat, 20 Feb 2021 01:32:48 +0000 In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com> Message-Id: <20210220013255.1083202-3-matthewgarrett@google.com> Mime-Version: 1.0 References: <20210220013255.1083202-1-matthewgarrett@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH 2/9] tpm: Allow PCR 23 to be restricted to kernel-only use From: Matthew Garrett To: linux-kernel@vger.kernel.org Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org, keyrings@vger.kernel.org, zohar@linux.ibm.com, jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net, rjw@rjwysocki.net, Matthew Garrett , Matthew Garrett Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Under certain circumstances it might be desirable to enable the creation of TPM-backed secrets that are only accessible to the kernel. In an ideal world this could be achieved by using TPM localities, but these don't appear to be available on consumer systems. An alternative is to simply block userland from modifying one of the resettable PCRs, leaving it available to the kernel. If the kernel ensures that no userland can access the TPM while it is carrying out work, it can reset PCR 23, extend it to an arbitrary value, create or load a secret, and then reset the PCR again. Even if userland somehow obtains the sealed material, it will be unable to unseal it since PCR 23 will never be in the appropriate state. Signed-off-by: Matthew Garrett --- drivers/char/tpm/Kconfig | 10 +++++++++ drivers/char/tpm/tpm-dev-common.c | 8 +++++++ drivers/char/tpm/tpm.h | 21 +++++++++++++++++++ drivers/char/tpm/tpm1-cmd.c | 35 +++++++++++++++++++++++++++++++ drivers/char/tpm/tpm2-cmd.c | 22 +++++++++++++++++++ drivers/char/tpm/tpm2-space.c | 2 +- 6 files changed, 97 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index a18c314da211..bba30fb16a2e 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -190,4 +190,14 @@ config TCG_FTPM_TEE This driver proxies for firmware TPM running in TEE. source "drivers/char/tpm/st33zp24/Kconfig" + +config TCG_TPM_RESTRICT_PCR + bool "Restrict userland access to PCR 23" + depends on TCG_TPM + help + If set, block userland from extending or resetting PCR 23. This + allows it to be restricted to in-kernel use, preventing userland + from being able to make use of data sealed to the TPM by the kernel. + This is required for secure hibernation support, but should be left + disabled if any userland may require access to PCR23. endif # TCG_TPM diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index 1784530b8387..d3db4fd76257 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -193,6 +193,14 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, priv->response_read = false; *off = 0; + if (priv->chip->flags & TPM_CHIP_FLAG_TPM2) + ret = tpm2_cmd_restricted(priv->chip, priv->data_buffer, size); + else + ret = tpm1_cmd_restricted(priv->chip, priv->data_buffer, size); + + if (ret) + goto out; + /* * If in nonblocking mode schedule an async job to send * the command return the size. diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 746f7696bdc0..8eed5016d733 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -232,6 +232,8 @@ 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_get_cc_attrs_tbl(struct tpm_chip *chip); +int tpm_find_and_validate_cc(struct tpm_chip *chip, struct tpm_space *space, + const void *buf, size_t bufsiz); int tpm2_find_cc(struct tpm_chip *chip, u32 cc); int tpm2_init_space(struct tpm_space *space, unsigned int buf_size); void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space); @@ -245,4 +247,23 @@ void tpm_bios_log_setup(struct tpm_chip *chip); void tpm_bios_log_teardown(struct tpm_chip *chip); int tpm_dev_common_init(void); void tpm_dev_common_exit(void); + +#ifdef CONFIG_TCG_TPM_RESTRICT_PCR +#define TPM_RESTRICTED_PCR 23 + +int tpm1_cmd_restricted(struct tpm_chip *chip, u8 *buffer, size_t size); +int tpm2_cmd_restricted(struct tpm_chip *chip, u8 *buffer, size_t size); +#else +static inline int tpm1_cmd_restricted(struct tpm_chip *chip, u8 *buffer, + size_t size) +{ + return 0; +} + +static inline int tpm2_cmd_restricted(struct tpm_chip *chip, u8 *buffer, + size_t size) +{ + return 0; +} +#endif #endif diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index 36990e9d2dc1..2dab1647d89c 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -840,3 +840,38 @@ int tpm1_get_pcr_allocation(struct tpm_chip *chip) return 0; } + +#ifdef CONFIG_TCG_TPM_RESTRICT_PCR +int tpm1_cmd_restricted(struct tpm_chip *chip, u8 *buffer, size_t size) +{ + struct tpm_header *header = (struct tpm_header *)buffer; + char len, offset; + u32 *pcr; + int pos; + + switch (be32_to_cpu(header->ordinal)) { + case TPM_ORD_PCR_EXTEND: + if (size < (TPM_HEADER_SIZE + sizeof(u32))) + return -EINVAL; + pcr = (u32 *)&buffer[TPM_HEADER_SIZE]; + if (be32_to_cpu(*pcr) == TPM_RESTRICTED_PCR) + return -EPERM; + break; + case TPM_ORD_PCR_RESET: + if (size < (TPM_HEADER_SIZE + 1)) + return -EINVAL; + len = buffer[TPM_HEADER_SIZE]; + if (size < (TPM_HEADER_SIZE + 1 + len)) + return -EINVAL; + offset = TPM_RESTRICTED_PCR/3; + if (len < offset) + break; + pos = TPM_HEADER_SIZE + 1 + offset; + if (buffer[pos] & (1 << (TPM_RESTRICTED_PCR - 2 * offset))) + return -EPERM; + break; + } + + return 0; +} +#endif diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 9609ae8086c6..7dbd4590dee8 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -795,3 +795,25 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc) return -1; } + +#ifdef CONFIG_TCG_TPM_RESTRICT_PCR +int tpm2_cmd_restricted(struct tpm_chip *chip, u8 *buffer, size_t size) +{ + int cc = tpm_find_and_validate_cc(chip, NULL, buffer, size); + u32 *handle; + + switch (cc) { + case TPM2_CC_PCR_EXTEND: + case TPM2_CC_PCR_RESET: + if (size < (TPM_HEADER_SIZE + sizeof(u32))) + return -EINVAL; + + handle = (u32 *)&buffer[TPM_HEADER_SIZE]; + if (be32_to_cpu(*handle) == TPM_RESTRICTED_PCR) + return -EPERM; + break; + } + + return 0; +} +#endif diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 784b8b3cb903..76a993492962 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -262,7 +262,7 @@ static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd) return 0; } -static int tpm_find_and_validate_cc(struct tpm_chip *chip, +int tpm_find_and_validate_cc(struct tpm_chip *chip, struct tpm_space *space, const void *cmd, size_t len) { From patchwork Sat Feb 20 01:32:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 12096509 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 54DEBC43381 for ; Sat, 20 Feb 2021 01:34:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3C60964E76 for ; Sat, 20 Feb 2021 01:34:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229876AbhBTBeb (ORCPT ); Fri, 19 Feb 2021 20:34:31 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44184 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229879AbhBTBe2 (ORCPT ); Fri, 19 Feb 2021 20:34:28 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E1DBCC061356 for ; Fri, 19 Feb 2021 17:33:11 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id y7so8742839ybh.20 for ; Fri, 19 Feb 2021 17:33:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=tPkjdQhNlqZVXDpP5yJgn/R3eqKmcVsk79AVXI8yHkQ=; b=Zx50wp6habmm6+el0TIzo0u7qOHlCOm21tfyU7IeCbHpyUo7RXM/ydodtfrTNKPyGb MhaJpJpsJf+vI14d2qHuFRiUFuReEp0S/K7CtcPKjs2EKEuZiNStl1e/msyn3l6N46Pj p1pQpnZkVz8QeYowO9xojdH5MWeLZnVwsz7xFNXX5JzKFmjpwj/wIMKIZlpinnc4YaJx ZGAUtAy0adcnnaSP8O2yLv71fABrXaKJrXh2EBvogj1lunWMUvSAtbM0uI7qGNBjc0h3 /AWBOgoRtEciq8n8xF7tXUUpNu91aBJPkMh0GE36bCFZgERd4YManl6OLsFCNlsclsa9 t6Cw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=tPkjdQhNlqZVXDpP5yJgn/R3eqKmcVsk79AVXI8yHkQ=; b=rFdP24MZsuABgANZFyn0ruspgnUKM8zuZc9oeaDlLOo+fvP6koAFzyW4x4UCMgUPmn WN4TIZJ7AHX82gcMZeirL61uVAdnCOBse3PHnto8RV2gl5vjvYKXUQvJ18HEbo2T5ni7 rP8+SSNsVhHkrTXcWSFgkGLmTrqzRpfsfHw5dQ0BHxhcQnxv30zb3kxUBPeGRbQ10BTl xXCzlzsf45VXIWCk9yaXLGXIvifrm+0TGaGHcYjXLs925Y/Vuidmq/EXlTY/XZZW+1XY 3JcyW43hlDi+fMb2JFnhXHaxQE38N8g4JtxiBCTE5GktWRcoFI28z+/d3B9h3FcTPBFu sJag== X-Gm-Message-State: AOAM532WYLCBIWuy7e4gG+s9eIRKk+KJtv54zMgKjrLI6T7cpqnM987R vtz8MZUD48KduUfGa9uTPFGqw7wgHDJuPvkaXQVeqQ== X-Google-Smtp-Source: ABdhPJzTMQoKiRt/GYbXbn1GNs0pzvQ+nRqhRk4fsDdWCk5ewihgfX4+8XyXioiXAte1nX5OIp6R/wPQ+jYr+bOPC7HGlw== Sender: "matthewgarrett via sendgmr" X-Received: from matthewgarrett-tmp.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:1081]) (user=matthewgarrett job=sendgmr) by 2002:a25:545:: with SMTP id 66mr17576310ybf.348.1613784791161; Fri, 19 Feb 2021 17:33:11 -0800 (PST) Date: Sat, 20 Feb 2021 01:32:49 +0000 In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com> Message-Id: <20210220013255.1083202-4-matthewgarrett@google.com> Mime-Version: 1.0 References: <20210220013255.1083202-1-matthewgarrett@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH 3/9] security: keys: trusted: Parse out individual components of the key blob From: Matthew Garrett To: linux-kernel@vger.kernel.org Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org, keyrings@vger.kernel.org, zohar@linux.ibm.com, jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net, rjw@rjwysocki.net, Matthew Garrett , Matthew Garrett Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Performing any sort of state validation of a sealed TPM blob requires being able to access the individual members in the response. Parse the blob sufficiently to be able to stash pointers to each member, along with the length. Signed-off-by: Matthew Garrett --- include/keys/trusted-type.h | 8 +++ security/keys/trusted-keys/trusted_tpm2.c | 67 ++++++++++++++++++++++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index a94c03a61d8f..020e01a99ea4 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -16,14 +16,22 @@ #define MAX_BLOB_SIZE 512 #define MAX_PCRINFO_SIZE 64 #define MAX_DIGEST_SIZE 64 +#define MAX_CREATION_DATA 412 +#define MAX_TK 76 struct trusted_key_payload { struct rcu_head rcu; unsigned int key_len; unsigned int blob_len; + unsigned int creation_len; + unsigned int creation_hash_len; + unsigned int tk_len; unsigned char migratable; unsigned char key[MAX_KEY_SIZE + 1]; unsigned char blob[MAX_BLOB_SIZE]; + unsigned char *creation; + unsigned char *creation_hash; + unsigned char *tk; }; struct trusted_key_options { diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 08ec7f48f01d..6357a51a24e9 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -50,6 +50,63 @@ static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle, tpm_buf_append(buf, hmac, hmac_len); } +static int tpm2_unpack_blob(struct trusted_key_payload *payload) +{ + int tmp, offset; + + /* Find the length of the private data */ + tmp = be16_to_cpup((__be16 *) &payload->blob[0]); + offset = tmp + 2; + if (offset > payload->blob_len) + return -EFAULT; + + /* Find the length of the public data */ + tmp = be16_to_cpup((__be16 *) &payload->blob[offset]); + offset += tmp + 2; + if (offset > payload->blob_len) + return -EFAULT; + + /* Find the length of the creation data and store it */ + tmp = be16_to_cpup((__be16 *) &payload->blob[offset]); + if (tmp > MAX_CREATION_DATA) + return -E2BIG; + + if ((offset + tmp + 2) > payload->blob_len) + return -EFAULT; + + payload->creation = &payload->blob[offset + 2]; + payload->creation_len = tmp; + offset += tmp + 2; + + /* Find the length of the creation hash and store it */ + tmp = be16_to_cpup((__be16 *) &payload->blob[offset]); + if (tmp > MAX_DIGEST_SIZE) + return -E2BIG; + + if ((offset + tmp + 2) > payload->blob_len) + return -EFAULT; + + payload->creation_hash = &payload->blob[offset + 2]; + payload->creation_hash_len = tmp; + offset += tmp + 2; + + /* + * Store the creation ticket. TPMT_TK_CREATION is two bytes of tag, + * four bytes of handle, and then the digest length and digest data + */ + tmp = be16_to_cpup((__be16 *) &payload->blob[offset + 6]); + if (tmp > MAX_TK) + return -E2BIG; + + if ((offset + tmp + 8) > payload->blob_len) + return -EFAULT; + + payload->tk = &payload->blob[offset]; + payload->tk_len = tmp + 8; + + return 0; +} + /** * tpm2_seal_trusted() - seal the payload of a trusted key * @@ -64,6 +121,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, struct trusted_key_options *options) { unsigned int blob_len; + unsigned int offset; struct tpm_buf buf; u32 hash; int i; @@ -139,14 +197,16 @@ int tpm2_seal_trusted(struct tpm_chip *chip, rc = -E2BIG; goto out; } - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) { + offset = TPM_HEADER_SIZE + 4; + if (tpm_buf_length(&buf) < offset + blob_len) { rc = -EFAULT; goto out; } - memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); + memcpy(payload->blob, &buf.data[offset], blob_len); payload->blob_len = blob_len; + rc = tpm2_unpack_blob(payload); out: tpm_buf_destroy(&buf); @@ -215,7 +275,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip, if (!rc) *blob_handle = be32_to_cpup( (__be32 *) &buf.data[TPM_HEADER_SIZE]); + else + goto out; + rc = tpm2_unpack_blob(payload); out: tpm_buf_destroy(&buf); From patchwork Sat Feb 20 01:32:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 12096517 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6619DC4332D for ; Sat, 20 Feb 2021 01:35:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4CA4E64EF7 for ; Sat, 20 Feb 2021 01:35:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230004AbhBTBfX (ORCPT ); Fri, 19 Feb 2021 20:35:23 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44332 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230009AbhBTBfI (ORCPT ); Fri, 19 Feb 2021 20:35:08 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 82DBEC061222 for ; Fri, 19 Feb 2021 17:33:13 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id y7so8742882ybh.20 for ; Fri, 19 Feb 2021 17:33:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=fRH0suW5jtV5qAv/9hzDuKsmxWRmfZBdn7mAB9P2pvE=; b=byWiim3QE0knNixnCWua94fMT6ZqVaWQM7DAWjI8sOF/A9MCSvBI6ckmBOAEqHssGM hpLvNfDbwchtlEaPziik0j00EJn1EhPQk4wQUt/FWCjsuQgvAPEeh2uyAlyAi/Co0kpd m6CT/ChIegkXc7m/O1waQRYP5baJ8f46ge3bnlerhAbJDgF3JJ0eQl1bvEJ8n3N/jbrC HZMRSqNOxfjhL4xt8gsd0Simd6OUQORBeFGGMI4s14aGQBg9KvIfER7H6GYbM6QIoXV9 THpQL8ebNwL2FyISBYTqsO1UKoPJRceLP+HROMTfo75rQ1+MmQFON+Fmj1G4VZPDBvqx MWeg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=fRH0suW5jtV5qAv/9hzDuKsmxWRmfZBdn7mAB9P2pvE=; b=sqeqXypGJelLlvmF7O908kTE/TqQk4oQM+IxznHSo3qbgwyzCiQfu1/nSMqmhbJ/Gy OVfbRgEEYond+i13JN9HgQA8IGkUFyFOZSIyO6nLtOpLkv0CVc8y7lNnyswZmDLOQAt6 KVLslgdGqsq1TwSjgkoQAPK48YtgEXq8wsGHIO0/R2X091oMq05RYL9WIY9q5sOq0WGd RybM9bJPMY+3IDOlx/tkFpfBVlFNqsGeRWIUvq++XxOm2Q8hlCCDZCxZXurcOhs8lqay hYR8ux8zjNKQQ8LWZR7igS+jq0NCogtPZ8sZc3IPP2ygYvhTliPEWCGv2TeqZc7oemqA 6rDA== X-Gm-Message-State: AOAM532CzK7I2CF7fi+Y4PxHHxegvL8qp90DF0ogcuBZ3GWZu8kmHyzi cI2QEzB5v2wdn8ued6xqYBS66p6bZfsux9Ayz1FUqA== X-Google-Smtp-Source: ABdhPJw0f5aK68gYqHj2EspA/X0qmwp/7lJNqvTB5aZ184iu3WsFJpaXKjgNIwrLodBt8D7h3iRs7a9ti+SgjWOQKlOogw== Sender: "matthewgarrett via sendgmr" X-Received: from matthewgarrett-tmp.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:1081]) (user=matthewgarrett job=sendgmr) by 2002:a05:6902:1003:: with SMTP id w3mr17858695ybt.445.1613784792782; Fri, 19 Feb 2021 17:33:12 -0800 (PST) Date: Sat, 20 Feb 2021 01:32:50 +0000 In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com> Message-Id: <20210220013255.1083202-5-matthewgarrett@google.com> Mime-Version: 1.0 References: <20210220013255.1083202-1-matthewgarrett@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH 4/9] security: keys: trusted: Store the handle of a loaded key From: Matthew Garrett To: linux-kernel@vger.kernel.org Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org, keyrings@vger.kernel.org, zohar@linux.ibm.com, jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net, rjw@rjwysocki.net, Matthew Garrett , Matthew Garrett Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Certain in-kernel operations using a trusted key (such as creation certification) require knowledge of the handle it's loaded at. Keep a copy of that in the payload. Signed-off-by: Matthew Garrett --- include/keys/trusted-type.h | 1 + security/keys/trusted-keys/trusted_tpm2.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index 020e01a99ea4..154d8a1769c3 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -21,6 +21,7 @@ struct trusted_key_payload { struct rcu_head rcu; + unsigned int blob_handle; unsigned int key_len; unsigned int blob_len; unsigned int creation_len; diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 6357a51a24e9..a3673fffd834 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -272,11 +272,13 @@ static int tpm2_load_cmd(struct tpm_chip *chip, } rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); - if (!rc) + if (!rc) { *blob_handle = be32_to_cpup( (__be32 *) &buf.data[TPM_HEADER_SIZE]); - else + payload->blob_handle = *blob_handle; + } else { goto out; + } rc = tpm2_unpack_blob(payload); out: From patchwork Sat Feb 20 01:32:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 12096513 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8D5E1C433E6 for ; Sat, 20 Feb 2021 01:35:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 519FB64E77 for ; Sat, 20 Feb 2021 01:35:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229961AbhBTBey (ORCPT ); Fri, 19 Feb 2021 20:34:54 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44176 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229934AbhBTBeo (ORCPT ); Fri, 19 Feb 2021 20:34:44 -0500 Received: from mail-qt1-x849.google.com (mail-qt1-x849.google.com [IPv6:2607:f8b0:4864:20::849]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 08D64C061225 for ; Fri, 19 Feb 2021 17:33:15 -0800 (PST) Received: by mail-qt1-x849.google.com with SMTP id l63so4435664qtd.12 for ; Fri, 19 Feb 2021 17:33:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=5s65rAUyafrnqP9PDtk4qfuDVzKOqtCy9uXyzty++eY=; b=ckXns8cEP+qoyBNXWqxZ/Pqz+1OzcyHa3rdo4kRUVo4uBYz2UPFHEl2UxhqLMHs44p SwCDNE39L1kZJAH0CwNAQuu6tKwNayENEqa5thHOiM1J3Voe6eSp81WNW43w4QqXp4ZS I6Mh8gcaQULV4BpLo5K27QD67/gr71IWSpXAojezrc7f2PXUet5rYCWvycSc3mR7/IB1 qA6isMB7s0Fxq76r7o7lY6pzocQiWmYDWhIQG4wJ/bH6L8YRjkJviJXx7kQdlxvBMsyp QaRkEb9Hy7UA9cSNBd6HWY6t4cV6SeE2VYRiuBw8BKsfRp++GlV62foOQK1zoqhWee/t qOGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=5s65rAUyafrnqP9PDtk4qfuDVzKOqtCy9uXyzty++eY=; b=o9CC9RJTKgVGYhZkjYJgCbd1bifmPh1wEIaJq2mF42OsuE6DldkWtfIBjP5Dk1zWv2 W7bac35gVl4VgEZrk7QGH4nEjEi7HrL5uYY1C0yoYHpy6ha3KjcpOIlspO5SU/M2gYUg T2tutJViWavMxqCGLb3XGuZjPoxSCL/s7LW95GgdwC0EtCS/yXtaZDcXQvW9HUkqujdk knCi35oaqM424gRibhy4On1ZBTRU4JdoNyb8XJOUSl4iL4FgOsibcHo2xUJcgUS45/ZY Nr5/sDW3fOCscVpe+daFobExI/qizHmtQQfvVZRGv1PcW+HwUFuiOmfStRunPmw+lKvq l8bA== X-Gm-Message-State: AOAM531hakAHZIWm5HFkNOzBIRDefAGC/Kt0GzkExtbK9O08I0tosCjR eBvpWSRsqQCGTpgdYaVWWsGcV+VR2RTCwyMufOCk6w== X-Google-Smtp-Source: ABdhPJxe0tcmAT4r+GJm84m4ZI8dalvzV9xz8hB5vIXENWytlqBxGoK9QHBaPGrOIOkjE3sq0edeDJIlSBq6GZPZhGFw4g== Sender: "matthewgarrett via sendgmr" X-Received: from matthewgarrett-tmp.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:1081]) (user=matthewgarrett job=sendgmr) by 2002:a0c:ed4f:: with SMTP id v15mr4210810qvq.55.1613784794251; Fri, 19 Feb 2021 17:33:14 -0800 (PST) Date: Sat, 20 Feb 2021 01:32:51 +0000 In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com> Message-Id: <20210220013255.1083202-6-matthewgarrett@google.com> Mime-Version: 1.0 References: <20210220013255.1083202-1-matthewgarrett@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH 5/9] security: keys: trusted: Allow storage of PCR values in creation data From: Matthew Garrett To: linux-kernel@vger.kernel.org Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org, keyrings@vger.kernel.org, zohar@linux.ibm.com, jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net, rjw@rjwysocki.net, Matthew Garrett , Matthew Garrett Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org When TPMs generate keys, they can also generate some information describing the state of the PCRs at creation time. This data can then later be certified by the TPM, allowing verification of the PCR values. This allows us to determine the state of the system at the time a key was generated. Add an additional argument to the trusted key creation options, allowing the user to provide the set of PCRs that should have their values incorporated into the creation data. Signed-off-by: Matthew Garrett --- .../security/keys/trusted-encrypted.rst | 4 +++ include/keys/trusted-type.h | 1 + security/keys/trusted-keys/trusted_tpm1.c | 9 +++++++ security/keys/trusted-keys/trusted_tpm2.c | 25 +++++++++++++++++-- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst index 1da879a68640..27bc43463ec8 100644 --- a/Documentation/security/keys/trusted-encrypted.rst +++ b/Documentation/security/keys/trusted-encrypted.rst @@ -72,6 +72,10 @@ Usage:: policyhandle= handle to an authorization policy session that defines the same policy and with the same hash algorithm as was used to seal the key. + creationpcrs= hex integer representing the set of PCR values to be + included in the PCR creation data. The bit corresponding + to each PCR should be 1 to be included, 0 to be ignored. + TPM2 only. "keyctl print" returns an ascii hex copy of the sealed key, which is in standard TPM_STORED_DATA format. The key length for new keys are always in bytes. diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index 154d8a1769c3..875e05f33b84 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -47,6 +47,7 @@ struct trusted_key_options { uint32_t policydigest_len; unsigned char policydigest[MAX_DIGEST_SIZE]; uint32_t policyhandle; + uint32_t creation_pcrs; }; extern struct key_type key_type_trusted; diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index 74d82093cbaa..3d371ab3441f 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -709,6 +709,7 @@ enum { Opt_hash, Opt_policydigest, Opt_policyhandle, + Opt_creationpcrs, }; static const match_table_t key_tokens = { @@ -724,6 +725,7 @@ static const match_table_t key_tokens = { {Opt_hash, "hash=%s"}, {Opt_policydigest, "policydigest=%s"}, {Opt_policyhandle, "policyhandle=%s"}, + {Opt_creationpcrs, "creationpcrs=%s"}, {Opt_err, NULL} }; @@ -834,6 +836,13 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return -EINVAL; opt->policyhandle = handle; break; + case Opt_creationpcrs: + if (!tpm2) + return -EINVAL; + res = kstrtoint(args[0].from, 16, &opt->creation_pcrs); + if (res < 0) + return -EINVAL; + break; default: return -EINVAL; } diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index a3673fffd834..282f956ad610 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -124,7 +124,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, unsigned int offset; struct tpm_buf buf; u32 hash; - int i; + int i, j; int rc; for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) { @@ -181,7 +181,28 @@ int tpm2_seal_trusted(struct tpm_chip *chip, tpm_buf_append_u16(&buf, 0); /* creation PCR */ - tpm_buf_append_u32(&buf, 0); + if (options->creation_pcrs) { + /* One bank */ + tpm_buf_append_u32(&buf, 1); + /* Which bank to use */ + tpm_buf_append_u16(&buf, hash); + /* Length of the PCR bitmask */ + tpm_buf_append_u8(&buf, 3); + /* PCR bitmask */ + for (i = 0; i < 3; i++) { + char tmp = 0; + + for (j = 0; j < 8; j++) { + char bit = (i * 8) + j; + + if (options->creation_pcrs & (1 << bit)) + tmp |= (1 << j); + } + tpm_buf_append_u8(&buf, tmp); + } + } else { + tpm_buf_append_u32(&buf, 0); + } if (buf.flags & TPM_BUF_OVERFLOW) { rc = -E2BIG; From patchwork Sat Feb 20 01:32:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 12096521 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B746EC433E9 for ; Sat, 20 Feb 2021 01:35:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 81D4464E77 for ; Sat, 20 Feb 2021 01:35:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230049AbhBTBfc (ORCPT ); Fri, 19 Feb 2021 20:35:32 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44348 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230017AbhBTBfK (ORCPT ); Fri, 19 Feb 2021 20:35:10 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E2CA8C0611C3 for ; Fri, 19 Feb 2021 17:33:16 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id l10so8921119ybt.6 for ; Fri, 19 Feb 2021 17:33:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=yAS0db83NOvwvM9lfRGQuy1QnGkOfJS/tz8Vl+zvFMk=; b=vNMYaLsjV2HvgP0sU96BmGUNkZ6giTH4K10g080lTn/Ugb7UbVG1ZlvV4WQUuihihT m/Of1WZXYlY3a9WXvW/e100aspdOcBBqvCWyQknfjbya4DeoWSqRZ4LLM2Dkn6EmyC9M NaisJ7C7DWPUWaz2i36fHHI7GDLnMmuX5U1qZUQWPwxfAKKJvt0mrhhWNV5bu5x99ECh NlVzJIRL/7TgBsTBiPS5iSXAx/IpWGiFyQ2lpnAHVJQuwdLLLFUBDS71XaRG5bAwTiQk q7n6YgdBj2RTdLkeGuagVsRkvL+GT9ojM6M8/2RIpGYRLIVlxL6fSJBzwex/anhYPN5y iJQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=yAS0db83NOvwvM9lfRGQuy1QnGkOfJS/tz8Vl+zvFMk=; b=G23EuTl64sZaTPlxqAVKNBnY7yKCntRR4UIS3S4HnI2H5t5j5IrVsUu/yGPLXABtZo 569k4h8VLcBKAQtUcwzIaKKtZF8PKcy03OlR/rymsJ2kl1CB5lEVbI5AEsNu9dgGu9WK 9Ryx5KBdab0rI5UxyKMM0FXIh686ru12agig12K7viXElsA/r9iWR8+Io/qR5fCXc32L wG8GC4QjKYhq9tbZ0P+NTugErPRk+L6/yVkJp4NrGDl/KLnGguFEjCSNkArheDFRjZJG K5b6gbFR/GAsm3inLbluOte2mHd3ZrEnSw/8043TX9sQ0IemqpLNq2gRcmROO4OSZEa6 MHow== X-Gm-Message-State: AOAM5304zWPxFT7YGEqQ0gu1ZL77k7JdyZYMAX0QyMXn2dlIPF9aBg8D qeVi5wYS6iUmedm0PRW4thFVz3gcUYYlOkLN8Wcykg== X-Google-Smtp-Source: ABdhPJylvuxVey2oMDy3Y+4tyu3KCIe8082QL0jCnfFGtWVDdwBQo9cOVp5MYVFW43mTvmmgfPXtg9tpiDL2UBytgJbjpA== Sender: "matthewgarrett via sendgmr" X-Received: from matthewgarrett-tmp.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:1081]) (user=matthewgarrett job=sendgmr) by 2002:a25:dad2:: with SMTP id n201mr16502549ybf.470.1613784796094; Fri, 19 Feb 2021 17:33:16 -0800 (PST) Date: Sat, 20 Feb 2021 01:32:52 +0000 In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com> Message-Id: <20210220013255.1083202-7-matthewgarrett@google.com> Mime-Version: 1.0 References: <20210220013255.1083202-1-matthewgarrett@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH 6/9] pm: hibernate: Optionally store and verify a hash of the image From: Matthew Garrett To: linux-kernel@vger.kernel.org Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org, keyrings@vger.kernel.org, zohar@linux.ibm.com, jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net, rjw@rjwysocki.net, Matthew Garrett , Matthew Garrett Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Calculate and store a cryptographically secure hash of the hibernation image if SF_VERIFY_IMAGE is set in the hibernation flags. This allows detection of a corrupt image, but has the disadvantage that it requires the blocks be read in in linear order. Signed-off-by: Matthew Garrett --- kernel/power/power.h | 1 + kernel/power/swap.c | 131 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 110 insertions(+), 22 deletions(-) diff --git a/kernel/power/power.h b/kernel/power/power.h index 778bf431ec02..b8e00b9dcee8 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -168,6 +168,7 @@ extern int swsusp_swap_in_use(void); #define SF_PLATFORM_MODE 1 #define SF_NOCOMPRESS_MODE 2 #define SF_CRC32_MODE 4 +#define SF_VERIFY_IMAGE 8 /* kernel/power/hibernate.c */ extern int swsusp_check(void); diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 72e33054a2e1..a13241a20567 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include "power.h" @@ -95,17 +97,20 @@ struct swap_map_page_list { struct swap_map_handle { struct swap_map_page *cur; struct swap_map_page_list *maps; + struct shash_desc *desc; sector_t cur_swap; sector_t first_sector; unsigned int k; unsigned long reqd_free_pages; u32 crc32; + u8 digest[SHA256_DIGEST_SIZE]; }; struct swsusp_header { char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int) - - sizeof(u32)]; + sizeof(u32) - SHA256_DIGEST_SIZE]; u32 crc32; + u8 digest[SHA256_DIGEST_SIZE]; sector_t image; unsigned int flags; /* Flags to pass to the "boot" kernel */ char orig_sig[10]; @@ -305,6 +310,9 @@ static blk_status_t hib_wait_io(struct hib_bio_batch *hb) * We are relying on the behavior of blk_plug that a thread with * a plug will flush the plug list before sleeping. */ + if (!hb) + return 0; + wait_event(hb->wait, atomic_read(&hb->count) == 0); return blk_status_to_errno(hb->error); } @@ -327,6 +335,8 @@ static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags) swsusp_header->flags = flags; if (flags & SF_CRC32_MODE) swsusp_header->crc32 = handle->crc32; + memcpy(swsusp_header->digest, handle->digest, + SHA256_DIGEST_SIZE); error = hib_submit_io(REQ_OP_WRITE, REQ_SYNC, swsusp_resume_block, swsusp_header, NULL); } else { @@ -417,6 +427,7 @@ static void release_swap_writer(struct swap_map_handle *handle) static int get_swap_writer(struct swap_map_handle *handle) { int ret; + struct crypto_shash *tfm; ret = swsusp_swap_check(); if (ret) { @@ -437,7 +448,28 @@ static int get_swap_writer(struct swap_map_handle *handle) handle->k = 0; handle->reqd_free_pages = reqd_free_pages(); handle->first_sector = handle->cur_swap; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) { + ret = -EINVAL; + goto err_rel; + } + handle->desc = kmalloc(sizeof(struct shash_desc) + + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!handle->desc) { + ret = -ENOMEM; + goto err_rel; + } + + handle->desc->tfm = tfm; + + ret = crypto_shash_init(handle->desc); + if (ret != 0) + goto err_free; + return 0; +err_free: + kfree(handle->desc); err_rel: release_swap_writer(handle); err_close: @@ -446,7 +478,7 @@ static int get_swap_writer(struct swap_map_handle *handle) } static int swap_write_page(struct swap_map_handle *handle, void *buf, - struct hib_bio_batch *hb) + struct hib_bio_batch *hb, bool hash) { int error = 0; sector_t offset; @@ -454,6 +486,7 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, if (!handle->cur) return -EINVAL; offset = alloc_swapdev_block(root_swap); + crypto_shash_update(handle->desc, buf, PAGE_SIZE); error = write_page(buf, offset, hb); if (error) return error; @@ -496,6 +529,7 @@ static int flush_swap_writer(struct swap_map_handle *handle) static int swap_writer_finish(struct swap_map_handle *handle, unsigned int flags, int error) { + crypto_shash_final(handle->desc, handle->digest); if (!error) { pr_info("S"); error = mark_swapfiles(handle, flags); @@ -560,7 +594,7 @@ static int save_image(struct swap_map_handle *handle, ret = snapshot_read_next(snapshot); if (ret <= 0) break; - ret = swap_write_page(handle, data_of(*snapshot), &hb); + ret = swap_write_page(handle, data_of(*snapshot), &hb, true); if (ret) break; if (!(nr_pages % m)) @@ -844,7 +878,7 @@ static int save_image_lzo(struct swap_map_handle *handle, off += PAGE_SIZE) { memcpy(page, data[thr].cmp + off, PAGE_SIZE); - ret = swap_write_page(handle, page, &hb); + ret = swap_write_page(handle, page, &hb, true); if (ret) goto out_finish; } @@ -938,7 +972,7 @@ int swsusp_write(unsigned int flags) goto out_finish; } header = (struct swsusp_info *)data_of(snapshot); - error = swap_write_page(&handle, header, NULL); + error = swap_write_page(&handle, header, NULL, false); if (!error) { error = (flags & SF_NOCOMPRESS_MODE) ? save_image(&handle, &snapshot, pages - 1) : @@ -974,6 +1008,7 @@ static int get_swap_reader(struct swap_map_handle *handle, int error; struct swap_map_page_list *tmp, *last; sector_t offset; + struct crypto_shash *tfm; *flags_p = swsusp_header->flags; @@ -1011,11 +1046,34 @@ static int get_swap_reader(struct swap_map_handle *handle, } handle->k = 0; handle->cur = handle->maps->map; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) { + error = -EINVAL; + goto err_rel; + } + handle->desc = kmalloc(sizeof(struct shash_desc) + + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!handle->desc) { + error = -ENOMEM; + goto err_rel; + } + + handle->desc->tfm = tfm; + + error = crypto_shash_init(handle->desc); + if (error != 0) + goto err_free; return 0; +err_free: + kfree(handle->desc); +err_rel: + release_swap_reader(handle); + return error; } static int swap_read_page(struct swap_map_handle *handle, void *buf, - struct hib_bio_batch *hb) + struct hib_bio_batch *hb, bool hash) { sector_t offset; int error; @@ -1029,6 +1087,7 @@ static int swap_read_page(struct swap_map_handle *handle, void *buf, error = hib_submit_io(REQ_OP_READ, 0, offset, buf, hb); if (error) return error; + crypto_shash_update(handle->desc, buf, PAGE_SIZE); if (++handle->k >= MAP_PAGE_ENTRIES) { handle->k = 0; free_page((unsigned long)handle->maps->map); @@ -1043,11 +1102,21 @@ static int swap_read_page(struct swap_map_handle *handle, void *buf, return error; } -static int swap_reader_finish(struct swap_map_handle *handle) +static int swap_reader_finish(struct swap_map_handle *handle, + struct swsusp_info *header) { + int ret = 0; + + crypto_shash_final(handle->desc, handle->digest); + if (memcmp(handle->digest, swsusp_header->digest, + SHA256_DIGEST_SIZE) != 0) { + pr_err("Image digest doesn't match header digest\n"); + ret = -ENODATA; + } + release_swap_reader(handle); - return 0; + return ret; } /** @@ -1064,11 +1133,20 @@ static int load_image(struct swap_map_handle *handle, int ret = 0; ktime_t start; ktime_t stop; - struct hib_bio_batch hb; + struct hib_bio_batch *hb, real_hb; int err2; unsigned nr_pages; - hib_init_batch(&hb); + /* + * If we're calculating the SHA256 of the image, we need the blocks + * to be read in in order + */ + if (swsusp_header->flags & SF_VERIFY_IMAGE) { + hb = NULL; + } else { + hib_init_batch(&real_hb); + hb = &real_hb; + } clean_pages_on_read = true; pr_info("Loading image data pages (%u pages)...\n", nr_to_read); @@ -1081,11 +1159,11 @@ static int load_image(struct swap_map_handle *handle, ret = snapshot_write_next(snapshot); if (ret <= 0) break; - ret = swap_read_page(handle, data_of(*snapshot), &hb); + ret = swap_read_page(handle, data_of(*snapshot), hb, true); if (ret) break; if (snapshot->sync_read) - ret = hib_wait_io(&hb); + ret = hib_wait_io(hb); if (ret) break; if (!(nr_pages % m)) @@ -1093,8 +1171,8 @@ static int load_image(struct swap_map_handle *handle, nr_pages / m * 10); nr_pages++; } - err2 = hib_wait_io(&hb); - hib_finish_batch(&hb); + err2 = hib_wait_io(hb); + hib_finish_batch(hb); stop = ktime_get(); if (!ret) ret = err2; @@ -1169,7 +1247,7 @@ static int load_image_lzo(struct swap_map_handle *handle, unsigned int m; int ret = 0; int eof = 0; - struct hib_bio_batch hb; + struct hib_bio_batch *hb, real_hb; ktime_t start; ktime_t stop; unsigned nr_pages; @@ -1182,7 +1260,16 @@ static int load_image_lzo(struct swap_map_handle *handle, struct dec_data *data = NULL; struct crc_data *crc = NULL; - hib_init_batch(&hb); + /* + * If we're calculating the SHA256 of the image, we need the blocks + * to be read in in order + */ + if (swsusp_header->flags & SF_VERIFY_IMAGE) { + hb = NULL; + } else { + hib_init_batch(&real_hb); + hb = &real_hb; + } /* * We'll limit the number of threads for decompression to limit memory @@ -1301,7 +1388,7 @@ static int load_image_lzo(struct swap_map_handle *handle, for(;;) { for (i = 0; !eof && i < want; i++) { - ret = swap_read_page(handle, page[ring], &hb); + ret = swap_read_page(handle, page[ring], hb, true); if (ret) { /* * On real read error, finish. On end of data, @@ -1328,7 +1415,7 @@ static int load_image_lzo(struct swap_map_handle *handle, if (!asked) break; - ret = hib_wait_io(&hb); + ret = hib_wait_io(hb); if (ret) goto out_finish; have += asked; @@ -1382,7 +1469,7 @@ static int load_image_lzo(struct swap_map_handle *handle, * Wait for more data while we are decompressing. */ if (have < LZO_CMP_PAGES && asked) { - ret = hib_wait_io(&hb); + ret = hib_wait_io(hb); if (ret) goto out_finish; have += asked; @@ -1458,7 +1545,7 @@ static int load_image_lzo(struct swap_map_handle *handle, } swsusp_show_speed(start, stop, nr_to_read, "Read"); out_clean: - hib_finish_batch(&hb); + hib_finish_batch(hb); for (i = 0; i < ring_size; i++) free_page((unsigned long)page[i]); if (crc) { @@ -1499,13 +1586,13 @@ int swsusp_read(unsigned int *flags_p) if (error) goto end; if (!error) - error = swap_read_page(&handle, header, NULL); + error = swap_read_page(&handle, header, NULL, false); if (!error) { error = (*flags_p & SF_NOCOMPRESS_MODE) ? load_image(&handle, &snapshot, header->pages - 1) : load_image_lzo(&handle, &snapshot, header->pages - 1); } - swap_reader_finish(&handle); + error = swap_reader_finish(&handle, header); end: if (!error) pr_debug("Image successfully loaded\n"); From patchwork Sat Feb 20 01:32:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 12096523 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D451DC433E0 for ; Sat, 20 Feb 2021 01:35:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9F15E64EE5 for ; Sat, 20 Feb 2021 01:35:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230060AbhBTBff (ORCPT ); Fri, 19 Feb 2021 20:35:35 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44350 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230018AbhBTBfK (ORCPT ); Fri, 19 Feb 2021 20:35:10 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A86DCC0611BD for ; Fri, 19 Feb 2021 17:33:18 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id x4so8800147ybj.22 for ; Fri, 19 Feb 2021 17:33:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=+fj/Z6ua/xhvW/t0EkMNvoSpSDcWQcmaOe23w27DhCU=; b=tNj9cgZgEKD8n70OZsmqKRRl3GWLRgFQFMJXrCLDOI3GLrM+YESsK63Ocq/x3+Fdoq nCD/3sccAVAjp1dB6HczG/FsyOtZCtDm2eqdOaSQjgNScvpAvUzRn9R3yEuZbaC2Fxze psgUxP1b58V6t9kcxnU1Pd1q3TO0BNdUlwtCsPy67epV5Akx7oJPOPpQg+o9M2/B7cqM VI4zp7Pb7bZv/NY4zD8ZF7G1OtlIp6zLwlOoH2Tk/ZD8yesyDUA0zgZMGeUOcMADwW5v fa9PB5YDdXirQoJSNDMpmpnbyvfw3kmGjnX8TGmLsPSbUs5rHvTSSFp84Kt0/kIvaTEq gNig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=+fj/Z6ua/xhvW/t0EkMNvoSpSDcWQcmaOe23w27DhCU=; b=A9zBaBvdB/dXS/QqtVz5vGSWwZQV/sWapIlQmURRcUZIQgPRyptyhkDMLdoplhJDgD tt6nfBtsafFMXZc4XdyHUp79o3XCPvZo3dXQYKiYeRe9xrG2PfXeSb7ECJWFZytNYSdE iSqaXNK7tBGHRrY7NKFa5ejr6OH1sFAj3lL4JISB/VHCl9jIrERKCoxmjJ3X8evGL21D Jo59TGpUTurmDF8ySdq4qFVKLlGyoyYQodKXGyUXjwTf97HZopd2wNy2BgF1pkwoa2aO 7j2c2Vi9lnyxYBvRhOet5RdGQYxbf5X6lGuqyLmbVy4wfk6soiwe9OIPF0ikXRMRC6g0 YMqQ== X-Gm-Message-State: AOAM530P1Tge3E9cg2bXS6EvN+Hfrbz3Daug49nYrEB+zjjml+PaRtl8 XlQWODspM6hk8T/Ms28VtgPdy1m0qD9u3nuiP2BAhw== X-Google-Smtp-Source: ABdhPJwoq5Xgs9mVLQPRLuL0fs+U1jl6Dz4XXx/0DoMGONnIQm4b8mNyDg4/g6CBwb67UiEgZB39EuO0+kA9Vwt6DawgCQ== Sender: "matthewgarrett via sendgmr" X-Received: from matthewgarrett-tmp.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:1081]) (user=matthewgarrett job=sendgmr) by 2002:a25:741:: with SMTP id 62mr19123176ybh.469.1613784797905; Fri, 19 Feb 2021 17:33:17 -0800 (PST) Date: Sat, 20 Feb 2021 01:32:53 +0000 In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com> Message-Id: <20210220013255.1083202-8-matthewgarrett@google.com> Mime-Version: 1.0 References: <20210220013255.1083202-1-matthewgarrett@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH 7/9] pm: hibernate: Optionally use TPM-backed keys to protect image integrity From: Matthew Garrett To: linux-kernel@vger.kernel.org Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org, keyrings@vger.kernel.org, zohar@linux.ibm.com, jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net, rjw@rjwysocki.net, Matthew Garrett , Matthew Garrett Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org A plain hash protects the hibernation image against accidental modification, but in the face of an active attack the hash can simply be updated to match the new image. Generate a random AES key and seal this with the TPM, and use it to encrypt the hash. On resume, the key can be unsealed and used to decrypt the hash. By setting PCR 23 to a specific value we can verify that the key used was generated by the kernel during hibernation and prevent an attacker providing their own key. Signed-off-by: Matthew Garrett --- kernel/power/Kconfig | 15 ++ kernel/power/Makefile | 1 + kernel/power/hibernate.c | 11 +- kernel/power/swap.c | 99 +++---------- kernel/power/swap.h | 38 +++++ kernel/power/tpm.c | 294 +++++++++++++++++++++++++++++++++++++++ kernel/power/tpm.h | 37 +++++ 7 files changed, 417 insertions(+), 78 deletions(-) create mode 100644 kernel/power/swap.h create mode 100644 kernel/power/tpm.c create mode 100644 kernel/power/tpm.h diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index a7320f07689d..0279cc10f319 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -92,6 +92,21 @@ config HIBERNATION_SNAPSHOT_DEV If in doubt, say Y. +config SECURE_HIBERNATION + bool "Implement secure hibernation support" + depends on HIBERNATION && TCG_TPM + select KEYS + select TRUSTED_KEYS + select CRYPTO + select CRYPTO_SHA256 + select CRYPTO_AES + select TCG_TPM_RESTRICT_PCR + help + Use a TPM-backed key to securely determine whether a hibernation + image was written out by the kernel and has not been tampered with. + This requires a TCG-compliant TPM2 device, which is present on most + modern hardware. + config PM_STD_PARTITION string "Default resume partition" depends on HIBERNATION diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 5899260a8bef..2edfef897607 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o obj-$(CONFIG_HIBERNATION_SNAPSHOT_DEV) += user.o +obj-$(CONFIG_SECURE_HIBERNATION) += tpm.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index da0b41914177..608bfbee38f5 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -34,6 +34,7 @@ #include #include "power.h" +#include "tpm.h" static int nocompress; @@ -81,7 +82,11 @@ void hibernate_release(void) bool hibernation_available(void) { - return nohibernate == 0 && !security_locked_down(LOCKDOWN_HIBERNATION); + if (security_locked_down(LOCKDOWN_HIBERNATION) && + !secure_hibernation_available()) + return false; + + return nohibernate == 0; } /** @@ -752,7 +757,9 @@ int hibernate(void) flags |= SF_NOCOMPRESS_MODE; else flags |= SF_CRC32_MODE; - +#ifdef CONFIG_SECURE_HIBERNATION + flags |= SF_VERIFY_IMAGE; +#endif pm_pr_dbg("Writing hibernation image.\n"); error = swsusp_write(flags); swsusp_free(); diff --git a/kernel/power/swap.c b/kernel/power/swap.c index a13241a20567..eaa585731314 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -32,9 +32,10 @@ #include #include #include -#include #include "power.h" +#include "swap.h" +#include "tpm.h" #define HIBERNATE_SIG "S1SUSPEND" @@ -89,34 +90,6 @@ struct swap_map_page_list { struct swap_map_page_list *next; }; -/** - * The swap_map_handle structure is used for handling swap in - * a file-alike way - */ - -struct swap_map_handle { - struct swap_map_page *cur; - struct swap_map_page_list *maps; - struct shash_desc *desc; - sector_t cur_swap; - sector_t first_sector; - unsigned int k; - unsigned long reqd_free_pages; - u32 crc32; - u8 digest[SHA256_DIGEST_SIZE]; -}; - -struct swsusp_header { - char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int) - - sizeof(u32) - SHA256_DIGEST_SIZE]; - u32 crc32; - u8 digest[SHA256_DIGEST_SIZE]; - sector_t image; - unsigned int flags; /* Flags to pass to the "boot" kernel */ - char orig_sig[10]; - char sig[10]; -} __packed; - static struct swsusp_header *swsusp_header; /** @@ -337,6 +310,9 @@ static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags) swsusp_header->crc32 = handle->crc32; memcpy(swsusp_header->digest, handle->digest, SHA256_DIGEST_SIZE); + error = swsusp_encrypt_digest(swsusp_header); + if (error) + return error; error = hib_submit_io(REQ_OP_WRITE, REQ_SYNC, swsusp_resume_block, swsusp_header, NULL); } else { @@ -427,7 +403,6 @@ static void release_swap_writer(struct swap_map_handle *handle) static int get_swap_writer(struct swap_map_handle *handle) { int ret; - struct crypto_shash *tfm; ret = swsusp_swap_check(); if (ret) { @@ -449,27 +424,11 @@ static int get_swap_writer(struct swap_map_handle *handle) handle->reqd_free_pages = reqd_free_pages(); handle->first_sector = handle->cur_swap; - tfm = crypto_alloc_shash("sha256", 0, 0); - if (IS_ERR(tfm)) { - ret = -EINVAL; - goto err_rel; - } - handle->desc = kmalloc(sizeof(struct shash_desc) + - crypto_shash_descsize(tfm), GFP_KERNEL); - if (!handle->desc) { - ret = -ENOMEM; + ret = swsusp_digest_setup(handle); + if (ret) goto err_rel; - } - - handle->desc->tfm = tfm; - - ret = crypto_shash_init(handle->desc); - if (ret != 0) - goto err_free; return 0; -err_free: - kfree(handle->desc); err_rel: release_swap_writer(handle); err_close: @@ -486,7 +445,7 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, if (!handle->cur) return -EINVAL; offset = alloc_swapdev_block(root_swap); - crypto_shash_update(handle->desc, buf, PAGE_SIZE); + swsusp_digest_update(handle, buf, PAGE_SIZE); error = write_page(buf, offset, hb); if (error) return error; @@ -529,7 +488,7 @@ static int flush_swap_writer(struct swap_map_handle *handle) static int swap_writer_finish(struct swap_map_handle *handle, unsigned int flags, int error) { - crypto_shash_final(handle->desc, handle->digest); + swsusp_digest_final(handle); if (!error) { pr_info("S"); error = mark_swapfiles(handle, flags); @@ -1008,7 +967,6 @@ static int get_swap_reader(struct swap_map_handle *handle, int error; struct swap_map_page_list *tmp, *last; sector_t offset; - struct crypto_shash *tfm; *flags_p = swsusp_header->flags; @@ -1047,27 +1005,12 @@ static int get_swap_reader(struct swap_map_handle *handle, handle->k = 0; handle->cur = handle->maps->map; - tfm = crypto_alloc_shash("sha256", 0, 0); - if (IS_ERR(tfm)) { - error = -EINVAL; - goto err_rel; - } - handle->desc = kmalloc(sizeof(struct shash_desc) + - crypto_shash_descsize(tfm), GFP_KERNEL); - if (!handle->desc) { - error = -ENOMEM; - goto err_rel; - } - - handle->desc->tfm = tfm; + error = swsusp_digest_setup(handle); + if (error) + goto err; - error = crypto_shash_init(handle->desc); - if (error != 0) - goto err_free; return 0; -err_free: - kfree(handle->desc); -err_rel: +err: release_swap_reader(handle); return error; } @@ -1087,7 +1030,7 @@ static int swap_read_page(struct swap_map_handle *handle, void *buf, error = hib_submit_io(REQ_OP_READ, 0, offset, buf, hb); if (error) return error; - crypto_shash_update(handle->desc, buf, PAGE_SIZE); + swsusp_digest_update(handle, buf, PAGE_SIZE); if (++handle->k >= MAP_PAGE_ENTRIES) { handle->k = 0; free_page((unsigned long)handle->maps->map); @@ -1107,11 +1050,13 @@ static int swap_reader_finish(struct swap_map_handle *handle, { int ret = 0; - crypto_shash_final(handle->desc, handle->digest); - if (memcmp(handle->digest, swsusp_header->digest, - SHA256_DIGEST_SIZE) != 0) { - pr_err("Image digest doesn't match header digest\n"); - ret = -ENODATA; + swsusp_digest_final(handle); + if (swsusp_header->flags & SF_VERIFY_IMAGE) { + if (memcmp(handle->digest, swsusp_header->digest, + SHA256_DIGEST_SIZE) != 0) { + pr_err("Image digest doesn't match header digest\n"); + ret = -ENODATA; + } } release_swap_reader(handle); @@ -1630,6 +1575,8 @@ int swsusp_check(void) error = -EINVAL; } + if (!error) + error = swsusp_decrypt_digest(swsusp_header); put: if (error) blkdev_put(hib_resume_bdev, FMODE_READ); diff --git a/kernel/power/swap.h b/kernel/power/swap.h new file mode 100644 index 000000000000..342189344f5f --- /dev/null +++ b/kernel/power/swap.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include +#include + +#ifndef _POWER_SWAP_H +#define _POWER_SWAP_H 1 +/** + * The swap_map_handle structure is used for handling swap in + * a file-alike way + */ + +struct swap_map_handle { + struct swap_map_page *cur; + struct swap_map_page_list *maps; + struct shash_desc *desc; + sector_t cur_swap; + sector_t first_sector; + unsigned int k; + unsigned long reqd_free_pages; + u32 crc32; + u8 digest[SHA256_DIGEST_SIZE]; +}; + +struct swsusp_header { + char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int) - + sizeof(u32) - SHA256_DIGEST_SIZE - MAX_BLOB_SIZE - + sizeof(u32)]; + u32 blob_len; + u8 blob[MAX_BLOB_SIZE]; + u8 digest[SHA256_DIGEST_SIZE]; + u32 crc32; + sector_t image; + unsigned int flags; /* Flags to pass to the "boot" kernel */ + char orig_sig[10]; + char sig[10]; +} __packed; + +#endif /* _POWER_SWAP_H */ diff --git a/kernel/power/tpm.c b/kernel/power/tpm.c new file mode 100644 index 000000000000..953dcbdc56d8 --- /dev/null +++ b/kernel/power/tpm.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include + +#include "swap.h" +#include "tpm.h" + +/* sha256("To sleep, perchance to dream") */ +static struct tpm_digest digest = { .alg_id = TPM_ALG_SHA256, + .digest = {0x92, 0x78, 0x3d, 0x79, 0x2d, 0x00, 0x31, 0xb0, 0x55, 0xf9, + 0x1e, 0x0d, 0xce, 0x83, 0xde, 0x1d, 0xc4, 0xc5, 0x8e, 0x8c, + 0xf1, 0x22, 0x38, 0x6c, 0x33, 0xb1, 0x14, 0xb7, 0xec, 0x05, + 0x5f, 0x49}}; + +struct skcipher_def { + struct scatterlist sg; + struct crypto_skcipher *tfm; + struct skcipher_request *req; + struct crypto_wait wait; +}; + +static int swsusp_enc_dec(struct trusted_key_payload *payload, char *buf, + int enc) +{ + struct skcipher_def sk; + struct crypto_skcipher *skcipher = NULL; + struct skcipher_request *req = NULL; + char *ivdata = NULL; + int ret; + + skcipher = crypto_alloc_skcipher("cbc-aes-aesni", 0, 0); + if (IS_ERR(skcipher)) + return PTR_ERR(skcipher); + + req = skcipher_request_alloc(skcipher, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto out; + } + + skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, + &sk.wait); + + /* AES 256 */ + if (crypto_skcipher_setkey(skcipher, payload->key, 32)) { + ret = -EAGAIN; + goto out; + } + + /* Key will never be re-used, just fix the IV to 0 */ + ivdata = kzalloc(16, GFP_KERNEL); + if (!ivdata) { + ret = -ENOMEM; + goto out; + } + + sk.tfm = skcipher; + sk.req = req; + + sg_init_one(&sk.sg, buf, 32); + skcipher_request_set_crypt(req, &sk.sg, &sk.sg, 16, ivdata); + crypto_init_wait(&sk.wait); + + /* perform the operation */ + if (enc) + ret = crypto_wait_req(crypto_skcipher_encrypt(sk.req), + &sk.wait); + else + ret = crypto_wait_req(crypto_skcipher_decrypt(sk.req), + &sk.wait); + + if (ret) + pr_info("skcipher encrypt returned with result %d\n", ret); + + goto out; + +out: + if (skcipher) + crypto_free_skcipher(skcipher); + if (req) + skcipher_request_free(req); + kfree(ivdata); + return ret; +} + +int swsusp_encrypt_digest(struct swsusp_header *header) +{ + const struct cred *cred = current_cred(); + struct trusted_key_payload *payload; + struct tpm_digest *digests = NULL; + struct tpm_chip *chip; + struct key *key; + int ret, i; + + char *keyinfo = "new\t32\tkeyhandle=0x81000001"; + + chip = tpm_default_chip(); + + if (!chip) + return -ENODEV; + + if (!(tpm_is_tpm2(chip))) + return -ENODEV; + + ret = tpm_pcr_reset(chip, 23); + if (ret != 0) + return ret; + + digests = kcalloc(chip->nr_allocated_banks, sizeof(struct tpm_digest), + GFP_KERNEL); + if (!digests) { + ret = -ENOMEM; + goto reset; + } + + for (i = 0; i <= chip->nr_allocated_banks; i++) { + digests[i].alg_id = chip->allocated_banks[i].alg_id; + if (digests[i].alg_id == digest.alg_id) + memcpy(&digests[i], &digest, sizeof(digest)); + } + + ret = tpm_pcr_extend(chip, 23, digests); + if (ret != 0) + goto reset; + + key = key_alloc(&key_type_trusted, "swsusp", GLOBAL_ROOT_UID, + GLOBAL_ROOT_GID, cred, 0, KEY_ALLOC_NOT_IN_QUOTA, + NULL); + + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto reset; + } + + ret = key_instantiate_and_link(key, keyinfo, strlen(keyinfo) + 1, NULL, + NULL); + if (ret < 0) + goto error; + + payload = key->payload.data[0]; + + ret = swsusp_enc_dec(payload, header->digest, 1); + if (ret) + goto error; + + memcpy(header->blob, payload->blob, payload->blob_len); + header->blob_len = payload->blob_len; + +error: + key_revoke(key); + key_put(key); +reset: + kfree(digests); + tpm_pcr_reset(chip, 23); + return ret; +} + +int swsusp_decrypt_digest(struct swsusp_header *header) +{ + const struct cred *cred = current_cred(); + char *keytemplate = "load\t%s\tkeyhandle=0x81000001"; + struct trusted_key_payload *payload; + struct tpm_digest *digests = NULL; + char *blobstring = NULL; + char *keyinfo = NULL; + struct tpm_chip *chip; + struct key *key; + int i, ret; + + chip = tpm_default_chip(); + + if (!chip) + return -ENODEV; + + if (!(tpm_is_tpm2(chip))) + return -ENODEV; + + ret = tpm_pcr_reset(chip, 23); + if (ret != 0) + return ret; + + digests = kcalloc(chip->nr_allocated_banks, sizeof(struct tpm_digest), + GFP_KERNEL); + if (!digests) + goto reset; + + for (i = 0; i <= chip->nr_allocated_banks; i++) { + digests[i].alg_id = chip->allocated_banks[i].alg_id; + if (digests[i].alg_id == digest.alg_id) + memcpy(&digests[i], &digest, sizeof(digest)); + } + + ret = tpm_pcr_extend(chip, 23, digests); + if (ret != 0) + goto reset; + + blobstring = kmalloc(header->blob_len * 2, GFP_KERNEL); + if (!blobstring) { + ret = -ENOMEM; + goto reset; + } + + bin2hex(blobstring, header->blob, header->blob_len); + + keyinfo = kasprintf(GFP_KERNEL, keytemplate, blobstring); + if (!keyinfo) { + ret = -ENOMEM; + goto reset; + } + + key = key_alloc(&key_type_trusted, "swsusp", GLOBAL_ROOT_UID, + GLOBAL_ROOT_GID, cred, 0, KEY_ALLOC_NOT_IN_QUOTA, + NULL); + + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto out; + } + + ret = key_instantiate_and_link(key, keyinfo, strlen(keyinfo) + 1, NULL, + NULL); + if (ret < 0) + goto out; + + payload = key->payload.data[0]; + + ret = swsusp_enc_dec(payload, header->digest, 0); + +out: + key_revoke(key); + key_put(key); +reset: + kfree(keyinfo); + kfree(blobstring); + kfree(digests); + tpm_pcr_reset(chip, 23); + return ret; +} + +int swsusp_digest_setup(struct swap_map_handle *handle) +{ + struct crypto_shash *tfm; + int ret; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + handle->desc = kmalloc(sizeof(struct shash_desc) + + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!handle->desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + handle->desc->tfm = tfm; + ret = crypto_shash_init(handle->desc); + if (ret != 0) { + crypto_free_shash(tfm); + kfree(handle->desc); + return ret; + } + + return 0; +} + +void swsusp_digest_update(struct swap_map_handle *handle, char *buf, + size_t size) +{ + crypto_shash_update(handle->desc, buf, size); +} + +void swsusp_digest_final(struct swap_map_handle *handle) +{ + crypto_shash_final(handle->desc, handle->digest); + crypto_free_shash(handle->desc->tfm); + kfree(handle->desc); +} + +int secure_hibernation_available(void) +{ + struct tpm_chip *chip = tpm_default_chip(); + + if (!chip) + return -ENODEV; + + if (!(tpm_is_tpm2(chip))) + return -ENODEV; + + return 0; +} diff --git a/kernel/power/tpm.h b/kernel/power/tpm.h new file mode 100644 index 000000000000..75b9140e5dc2 --- /dev/null +++ b/kernel/power/tpm.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include "swap.h" + +#ifndef _POWER_TPM_H +#define _POWER_TPM_H + +#ifdef CONFIG_SECURE_HIBERNATION +int secure_hibernation_available(void); +int swsusp_encrypt_digest(struct swsusp_header *header); +int swsusp_decrypt_digest(struct swsusp_header *header); +int swsusp_digest_setup(struct swap_map_handle *handle); +void swsusp_digest_update(struct swap_map_handle *handle, char *buf, + size_t size); +void swsusp_digest_final(struct swap_map_handle *handle); +#else +static inline int secure_hibernation_available(void) +{ + return -ENODEV; +}; +static inline int swsusp_encrypt_digest(struct swsusp_header *header) +{ + return 0; +} +static inline int swsusp_decrypt_digest(struct swsusp_header *header) +{ + return 0; +} +static inline int swsusp_digest_setup(struct swap_map_handle *handle) +{ + return 0; +} +static inline void swsusp_digest_update(struct swap_map_handle *handle, + char *buf, size_t size) {}; +static inline void swsusp_digest_final(struct swap_map_handle *handle) {}; +#endif + +#endif /* _POWER_TPM_H */ From patchwork Sat Feb 20 01:32:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 12096519 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F025BC43381 for ; Sat, 20 Feb 2021 01:35:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D81CF64E77 for ; Sat, 20 Feb 2021 01:35:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230019AbhBTBfh (ORCPT ); Fri, 19 Feb 2021 20:35:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44362 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230021AbhBTBfM (ORCPT ); Fri, 19 Feb 2021 20:35:12 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3322AC061A2B for ; Fri, 19 Feb 2021 17:33:20 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id u1so8818051ybu.14 for ; Fri, 19 Feb 2021 17:33:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=Le2xy86M2g7vFjnuX/RXj2Z9OeFij6CrkPXndbL+3YM=; b=mUmzPMLQ2EhM5ohqQmvRX3zzjTI4HWgSEU8OF6ChUOqqCnyRmUWuS7Fg85iVRVC6D0 tlS1Qf9auwUtImh2HiRyyIF9zKTBlptAOolf0FoHdiiUmi0OW/MbiiVc5aFxLp0Ixvg1 cFeCJz9zZMvDoZq8E4OJNEyCKW06WEsbbi1WGGYwxmWGValJTlOySL2JTQ+lyT9DZEcG H2uY8kjDRo7fTGbtIHU0o77/si0NRfV4D/hT8to4ifU0A4zu8NUO1F1mRowKzzBwornS TsK6HQUR5c3H2o4BOGqBx1XATTaJyYRtgg6nD/nS2R0cTSOvXH5a/+N4HO8Uwo08O0Ik vzfQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Le2xy86M2g7vFjnuX/RXj2Z9OeFij6CrkPXndbL+3YM=; b=AXFxqVMiKwkYjCDMRZlntzXN91aUjaZhniGUHf9yY5jBoUSOESvYzOpkp2/zQ7/a3M 1kF4A/IXevN02R7m5DlTDFc2hRl5STBfvZMnYMyDIBRqtyToF3yuU/V90BwyW0mEi7dV wr6nstSdZ/nMrnhsKRpfSh4CA1nzEuy4k8+fIfYZ6mCQlwxTE844+kmSmUAM5awsjpUU wTLngfAfdZ96NT+J2dzt9y638YvxYq4k6xJzg2IlzaStkuCxRo2phl+qxfXq1n/pqvkd Zdp4hl+ychII5ebh5DjDC3apKMqwbNy8B4ba1zimeSCfY1kTgwyIIXO7pnYo0VacRYt4 PhXg== X-Gm-Message-State: AOAM530RtBkJtVUC+iHbBYb+ehd9VtXopR1Z8H+oYsZvrYxkdNz/a8oK hloaLqSQJlLqpS0nIV3czQwb11yY1uRs4YXdhBX+HQ== X-Google-Smtp-Source: ABdhPJz+znzmydksCXNSz0qRUgqJOeUsiHxIZRQcS35YoWwljFjdXh5jNWJo2zOzCx4tGEmcAtw9+Y046tcbzA8xOA1QIg== Sender: "matthewgarrett via sendgmr" X-Received: from matthewgarrett-tmp.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:1081]) (user=matthewgarrett job=sendgmr) by 2002:a25:a0c9:: with SMTP id i9mr15696415ybm.479.1613784799467; Fri, 19 Feb 2021 17:33:19 -0800 (PST) Date: Sat, 20 Feb 2021 01:32:54 +0000 In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com> Message-Id: <20210220013255.1083202-9-matthewgarrett@google.com> Mime-Version: 1.0 References: <20210220013255.1083202-1-matthewgarrett@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH 8/9] pm: hibernate: Verify the digest encryption key From: Matthew Garrett To: linux-kernel@vger.kernel.org Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org, keyrings@vger.kernel.org, zohar@linux.ibm.com, jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net, rjw@rjwysocki.net, Matthew Garrett , Matthew Garrett Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org We want to ensure that the key used to encrypt the digest was created by the kernel during hibernation. To do this we request that the TPM include information about the value of PCR 23 at the time of key creation in the sealed blob. On resume, we can ask the TPM to certify that the creation data is accurate and then make sure that the PCR information in the blob corresponds to the expected value. Since only the kernel can touch PCR 23, if an attacker generates a key themselves the value of PCR 23 will have been different, allowing us to reject the key and boot normally instead of resuming. Signed-off-by: Matthew Garrett --- include/linux/tpm.h | 1 + kernel/power/tpm.c | 150 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/include/linux/tpm.h b/include/linux/tpm.h index e2075e2242a0..f6970986b097 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -216,6 +216,7 @@ enum tpm2_command_codes { TPM2_CC_SELF_TEST = 0x0143, TPM2_CC_STARTUP = 0x0144, TPM2_CC_SHUTDOWN = 0x0145, + TPM2_CC_CERTIFYCREATION = 0x014A, TPM2_CC_NV_READ = 0x014E, TPM2_CC_CREATE = 0x0153, TPM2_CC_LOAD = 0x0157, diff --git a/kernel/power/tpm.c b/kernel/power/tpm.c index 953dcbdc56d8..34e6cfb98ce4 100644 --- a/kernel/power/tpm.c +++ b/kernel/power/tpm.c @@ -14,6 +14,12 @@ static struct tpm_digest digest = { .alg_id = TPM_ALG_SHA256, 0xf1, 0x22, 0x38, 0x6c, 0x33, 0xb1, 0x14, 0xb7, 0xec, 0x05, 0x5f, 0x49}}; +/* sha256(sha256(empty_pcr | digest)) */ +static char expected_digest[] = {0x2f, 0x96, 0xf2, 0x1b, 0x70, 0xa9, 0xe8, + 0x42, 0x25, 0x8e, 0x66, 0x07, 0xbe, 0xbc, 0xe3, 0x1f, 0x2c, 0x84, 0x4a, + 0x3f, 0x85, 0x17, 0x31, 0x47, 0x9a, 0xa5, 0x53, 0xbb, 0x23, 0x0c, 0x32, + 0xf3}; + struct skcipher_def { struct scatterlist sg; struct crypto_skcipher *tfm; @@ -21,6 +27,39 @@ struct skcipher_def { struct crypto_wait wait; }; +static int sha256_data(char *buf, int size, char *output) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + int ret; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + desc = kmalloc(sizeof(struct shash_desc) + + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + desc->tfm = tfm; + ret = crypto_shash_init(desc); + if (ret != 0) { + crypto_free_shash(tfm); + kfree(desc); + return ret; + } + + crypto_shash_update(desc, buf, size); + crypto_shash_final(desc, output); + crypto_free_shash(desc->tfm); + kfree(desc); + + return 0; +} + static int swsusp_enc_dec(struct trusted_key_payload *payload, char *buf, int enc) { @@ -86,6 +125,58 @@ static int swsusp_enc_dec(struct trusted_key_payload *payload, char *buf, return ret; } +static int tpm_certify_creationdata(struct tpm_chip *chip, + struct trusted_key_payload *payload) +{ + struct tpm_header *head; + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CERTIFYCREATION); + if (rc) + return rc; + + /* Use TPM_RH_NULL for signHandle */ + tpm_buf_append_u32(&buf, 0x40000007); + + /* Object handle */ + tpm_buf_append_u32(&buf, payload->blob_handle); + + /* Auth */ + tpm_buf_append_u32(&buf, 9); + tpm_buf_append_u32(&buf, TPM2_RS_PW); + tpm_buf_append_u16(&buf, 0); + tpm_buf_append_u8(&buf, 0); + tpm_buf_append_u16(&buf, 0); + + /* Qualifying data */ + tpm_buf_append_u16(&buf, 0); + + /* Creation data hash */ + tpm_buf_append_u16(&buf, payload->creation_hash_len); + tpm_buf_append(&buf, payload->creation_hash, + payload->creation_hash_len); + + /* signature scheme */ + tpm_buf_append_u16(&buf, TPM_ALG_NULL); + + /* creation ticket */ + tpm_buf_append(&buf, payload->tk, payload->tk_len); + + rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); + if (rc) + goto out; + + head = (struct tpm_header *)buf.data; + + if (head->return_code != 0) + rc = -EINVAL; +out: + tpm_buf_destroy(&buf); + + return rc; +} + int swsusp_encrypt_digest(struct swsusp_header *header) { const struct cred *cred = current_cred(); @@ -95,7 +186,7 @@ int swsusp_encrypt_digest(struct swsusp_header *header) struct key *key; int ret, i; - char *keyinfo = "new\t32\tkeyhandle=0x81000001"; + char *keyinfo = "new\t32\tkeyhandle=0x81000001\tcreationpcrs=0x00800000"; chip = tpm_default_chip(); @@ -164,6 +255,7 @@ int swsusp_decrypt_digest(struct swsusp_header *header) char *keytemplate = "load\t%s\tkeyhandle=0x81000001"; struct trusted_key_payload *payload; struct tpm_digest *digests = NULL; + char certhash[SHA256_DIGEST_SIZE]; char *blobstring = NULL; char *keyinfo = NULL; struct tpm_chip *chip; @@ -184,8 +276,10 @@ int swsusp_decrypt_digest(struct swsusp_header *header) digests = kcalloc(chip->nr_allocated_banks, sizeof(struct tpm_digest), GFP_KERNEL); - if (!digests) + if (!digests) { + ret = -ENOMEM; goto reset; + } for (i = 0; i <= chip->nr_allocated_banks; i++) { digests[i].alg_id = chip->allocated_banks[i].alg_id; @@ -227,8 +321,58 @@ int swsusp_decrypt_digest(struct swsusp_header *header) payload = key->payload.data[0]; - ret = swsusp_enc_dec(payload, header->digest, 0); + ret = sha256_data(payload->creation, payload->creation_len, certhash); + if (ret < 0) + goto out; + + if (memcmp(payload->creation_hash, certhash, SHA256_DIGEST_SIZE) != 0) { + ret = -EINVAL; + goto out; + } + + ret = tpm_certify_creationdata(chip, payload); + if (ret != 0) { + ret = -EINVAL; + goto out; + } + + /* We now know that the creation data is authentic - parse it */ + + /* TPML_PCR_SELECTION.count */ + if (be32_to_cpu(*(int *)payload->creation) != 1) { + ret = -EINVAL; + goto out; + } + + if (be16_to_cpu(*(u16 *)&payload->creation[4]) != TPM_ALG_SHA256) { + ret = -EINVAL; + goto out; + } + + if (*(char *)&payload->creation[6] != 3) { + ret = -EINVAL; + goto out; + } + + /* PCR 23 selected */ + if (be32_to_cpu(*(int *)&payload->creation[6]) != 0x03000080) { + ret = -EINVAL; + goto out; + } + + if (be16_to_cpu(*(u16 *)&payload->creation[10]) != + SHA256_DIGEST_SIZE) { + ret = -EINVAL; + goto out; + } + if (memcmp(&payload->creation[12], expected_digest, + SHA256_DIGEST_SIZE) != 0) { + ret = -EINVAL; + goto out; + } + + ret = swsusp_enc_dec(payload, header->digest, 0); out: key_revoke(key); key_put(key); From patchwork Sat Feb 20 01:32:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 12096515 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 13304C433E6 for ; Sat, 20 Feb 2021 01:35:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DDBA464E77 for ; Sat, 20 Feb 2021 01:35:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230024AbhBTBfM (ORCPT ); Fri, 19 Feb 2021 20:35:12 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44172 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229996AbhBTBfE (ORCPT ); Fri, 19 Feb 2021 20:35:04 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DD784C061A2E for ; Fri, 19 Feb 2021 17:33:21 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id u17so8910099ybi.10 for ; Fri, 19 Feb 2021 17:33:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=AHjbHsJdHsjDWlT0meS/7wKVqgJA5qMsPwUMgz7CTrY=; b=DPjRo4j75pe5NPWUZw2PULq38PKScJgLe5C694z+Od25e1mgQhQ+0ZXITV67hlasB7 BHs9xe+ceb9RhAHRmdX19BKDLmd5FIXfgd8HemfRm4tTBBBAbKdirqtCV3bLbCZ6fMgH Fx4DgvBfX2gig6RPjueqXD+IlBOr8gwiqtvTUgGre0W6u7Vgp1DYhkYrStASE96rNlLp 1+kNlWDHPnwUYBF4nGUn+mChnYkhsFYkxPUUGh4bK1qbtcGhqDDiGUFIPRRT2WDqRi4T MtI3kHh6oBvhW7sOheb/oGp2WKxKuMWh9Rk58TkctHC7Apr/uCRNteVL4cLuYcZAVg/H 126Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=AHjbHsJdHsjDWlT0meS/7wKVqgJA5qMsPwUMgz7CTrY=; b=Z2mmFjb3OyMoUFSBk4ZK5h/ofJnr/+WcZOE4X2xMeXDpYh/Q/56f04NQpVgFOinspu J7zbrMb2KrnhJI+N2YLPV/eU4Pb7YlrRTBSdoyyqO98yFX8e5MG9iZyy5woJBL3cQkbv dwTNHBsfaQ2dAINxxKBZgKNyXIy9KeRWP/MSX61TU4Ti7FsojrSyg6LhTxMpUILK93Sx 7/wet+KmjTDLss7crhU59e1cAzbDSW2Jj4pLUO+CX2FCTwqDDqofGbKXoHasrvgvERXo /tX/c554E21W4Cz/p0fCwV5MvsQteTAuZiIA737o3PBurf+j1Tq7OVEXf9oAqL7RTFC0 XVlQ== X-Gm-Message-State: AOAM5300Q7wuOzY38LrvgYwWFSO9UmP6sQFjlDywvDuj1X2K/WNlfQlc v+bhMHQ2QuAC2z3nyhUcP3XHiw8AZU7wMnlxJ45g8g== X-Google-Smtp-Source: ABdhPJzDmDLe4ZPw7iSJPXc33ot7QHjnB/KrkLLd5Ij4DEcK/c6DsPaOS9aOJK39H4e6EnR2yDtA16zvqnpOF8cRLY0/9Q== Sender: "matthewgarrett via sendgmr" X-Received: from matthewgarrett-tmp.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:1081]) (user=matthewgarrett job=sendgmr) by 2002:a25:4112:: with SMTP id o18mr17366423yba.222.1613784801141; Fri, 19 Feb 2021 17:33:21 -0800 (PST) Date: Sat, 20 Feb 2021 01:32:55 +0000 In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com> Message-Id: <20210220013255.1083202-10-matthewgarrett@google.com> Mime-Version: 1.0 References: <20210220013255.1083202-1-matthewgarrett@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH 9/9] pm: hibernate: seal the encryption key with a PCR policy From: Matthew Garrett To: linux-kernel@vger.kernel.org Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org, keyrings@vger.kernel.org, zohar@linux.ibm.com, jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net, rjw@rjwysocki.net, Matthew Garrett , Matthew Garrett Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org The key blob is not secret, and by default the TPM will happily unseal it regardless of system state. We can protect against that by sealing the secret with a PCR policy - if the current PCR state doesn't match, the TPM will refuse to release the secret. For now let's just seal it to PCR 23. In the long term we may want a more flexible policy around this, such as including PCR 7. Signed-off-by: Matthew Garrett --- include/linux/tpm.h | 4 ++ kernel/power/tpm.c | 161 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 160 insertions(+), 5 deletions(-) diff --git a/include/linux/tpm.h b/include/linux/tpm.h index f6970986b097..2e0141978c87 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -225,18 +225,22 @@ enum tpm2_command_codes { TPM2_CC_CONTEXT_LOAD = 0x0161, TPM2_CC_CONTEXT_SAVE = 0x0162, TPM2_CC_FLUSH_CONTEXT = 0x0165, + TPM2_CC_START_AUTH_SESSION = 0x0176, TPM2_CC_VERIFY_SIGNATURE = 0x0177, TPM2_CC_GET_CAPABILITY = 0x017A, TPM2_CC_GET_RANDOM = 0x017B, TPM2_CC_PCR_READ = 0x017E, + TPM2_CC_POLICY_PCR = 0x017F, TPM2_CC_PCR_EXTEND = 0x0182, TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185, TPM2_CC_HASH_SEQUENCE_START = 0x0186, + TPM2_CC_POLICY_GET_DIGEST = 0x0189, TPM2_CC_CREATE_LOADED = 0x0191, TPM2_CC_LAST = 0x0193, /* Spec 1.36 */ }; enum tpm2_permanent_handles { + TPM2_RH_NULL = 0x40000007, TPM2_RS_PW = 0x40000009, }; diff --git a/kernel/power/tpm.c b/kernel/power/tpm.c index 34e6cfb98ce4..5de27c2f08be 100644 --- a/kernel/power/tpm.c +++ b/kernel/power/tpm.c @@ -125,6 +125,118 @@ static int swsusp_enc_dec(struct trusted_key_payload *payload, char *buf, return ret; } +static int tpm_setup_policy(struct tpm_chip *chip, int *session_handle) +{ + struct tpm_header *head; + struct tpm_buf buf; + char nonce[32] = {0x00}; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, + TPM2_CC_START_AUTH_SESSION); + if (rc) + return rc; + + /* Decrypt key */ + tpm_buf_append_u32(&buf, TPM2_RH_NULL); + + /* Auth entity */ + tpm_buf_append_u32(&buf, TPM2_RH_NULL); + + /* Nonce - blank is fine here */ + tpm_buf_append_u16(&buf, sizeof(nonce)); + tpm_buf_append(&buf, nonce, sizeof(nonce)); + + /* Encrypted secret - empty */ + tpm_buf_append_u16(&buf, 0); + + /* Policy type - session */ + tpm_buf_append_u8(&buf, 0x01); + + /* Encryption type - NULL */ + tpm_buf_append_u16(&buf, TPM_ALG_NULL); + + /* Hash type - SHA256 */ + tpm_buf_append_u16(&buf, TPM_ALG_SHA256); + + rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); + + if (rc) + goto out; + + head = (struct tpm_header *)buf.data; + + if (be32_to_cpu(head->length) != sizeof(struct tpm_header) + + sizeof(int) + sizeof(u16) + sizeof(nonce)) { + rc = -EINVAL; + goto out; + } + + *session_handle = be32_to_cpu(*(int *)&buf.data[10]); + memcpy(nonce, &buf.data[16], sizeof(nonce)); + + tpm_buf_destroy(&buf); + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_POLICY_PCR); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, *session_handle); + + /* PCR digest - read from the PCR, we'll verify creation data later */ + tpm_buf_append_u16(&buf, 0); + + /* One PCR */ + tpm_buf_append_u32(&buf, 1); + + /* SHA256 banks */ + tpm_buf_append_u16(&buf, TPM_ALG_SHA256); + + /* Select PCR 23 */ + tpm_buf_append_u32(&buf, 0x03000080); + + rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); + + if (rc) + goto out; + +out: + tpm_buf_destroy(&buf); + return rc; +} + +static int tpm_policy_get_digest(struct tpm_chip *chip, int handle, + char *digest) +{ + struct tpm_header *head; + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_POLICY_GET_DIGEST); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, handle); + + rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); + + if (rc) + goto out; + + head = (struct tpm_header *)buf.data; + if (be32_to_cpu(head->length) != sizeof(struct tpm_header) + + sizeof(u16) + SHA256_DIGEST_SIZE) { + rc = -EINVAL; + goto out; + } + + memcpy(digest, &buf.data[12], SHA256_DIGEST_SIZE); +out: + tpm_buf_destroy(&buf); + + return rc; +} + static int tpm_certify_creationdata(struct tpm_chip *chip, struct trusted_key_payload *payload) { @@ -182,11 +294,14 @@ int swsusp_encrypt_digest(struct swsusp_header *header) const struct cred *cred = current_cred(); struct trusted_key_payload *payload; struct tpm_digest *digests = NULL; + char policy[SHA256_DIGEST_SIZE]; + char *policydigest = NULL; struct tpm_chip *chip; struct key *key; + int session_handle; int ret, i; - - char *keyinfo = "new\t32\tkeyhandle=0x81000001\tcreationpcrs=0x00800000"; + char *keyinfo = NULL; + char *keytemplate = "new\t32\tkeyhandle=0x81000001\tcreationpcrs=0x00800000\tpolicydigest=%s"; chip = tpm_default_chip(); @@ -213,10 +328,35 @@ int swsusp_encrypt_digest(struct swsusp_header *header) memcpy(&digests[i], &digest, sizeof(digest)); } + policydigest = kmalloc(SHA256_DIGEST_SIZE * 2 + 1, GFP_KERNEL); + if (!policydigest) { + ret = -ENOMEM; + goto reset; + } + ret = tpm_pcr_extend(chip, 23, digests); if (ret != 0) goto reset; + ret = tpm_setup_policy(chip, &session_handle); + + if (ret != 0) + goto reset; + + ret = tpm_policy_get_digest(chip, session_handle, policy); + + if (ret != 0) + goto reset; + + bin2hex(policydigest, policy, SHA256_DIGEST_SIZE); + policydigest[64] = '\0'; + + keyinfo = kasprintf(GFP_KERNEL, keytemplate, policydigest); + if (!keyinfo) { + ret = -ENOMEM; + goto reset; + } + key = key_alloc(&key_type_trusted, "swsusp", GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, KEY_ALLOC_NOT_IN_QUOTA, NULL); @@ -228,6 +368,7 @@ int swsusp_encrypt_digest(struct swsusp_header *header) ret = key_instantiate_and_link(key, keyinfo, strlen(keyinfo) + 1, NULL, NULL); + if (ret < 0) goto error; @@ -244,6 +385,8 @@ int swsusp_encrypt_digest(struct swsusp_header *header) key_revoke(key); key_put(key); reset: + kfree(keyinfo); + kfree(policydigest); kfree(digests); tpm_pcr_reset(chip, 23); return ret; @@ -252,13 +395,14 @@ int swsusp_encrypt_digest(struct swsusp_header *header) int swsusp_decrypt_digest(struct swsusp_header *header) { const struct cred *cred = current_cred(); - char *keytemplate = "load\t%s\tkeyhandle=0x81000001"; + char *keytemplate = "load\t%s\tkeyhandle=0x81000001\tpolicyhandle=0x%x"; struct trusted_key_payload *payload; struct tpm_digest *digests = NULL; char certhash[SHA256_DIGEST_SIZE]; char *blobstring = NULL; char *keyinfo = NULL; struct tpm_chip *chip; + int session_handle; struct key *key; int i, ret; @@ -291,15 +435,22 @@ int swsusp_decrypt_digest(struct swsusp_header *header) if (ret != 0) goto reset; - blobstring = kmalloc(header->blob_len * 2, GFP_KERNEL); + ret = tpm_setup_policy(chip, &session_handle); + + if (ret != 0) + goto reset; + + blobstring = kmalloc(header->blob_len * 2 + 1, GFP_KERNEL); if (!blobstring) { ret = -ENOMEM; goto reset; } bin2hex(blobstring, header->blob, header->blob_len); + blobstring[header->blob_len * 2] = '\0'; - keyinfo = kasprintf(GFP_KERNEL, keytemplate, blobstring); + keyinfo = kasprintf(GFP_KERNEL, keytemplate, blobstring, + session_handle); if (!keyinfo) { ret = -ENOMEM; goto reset;