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: 12096487 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 1388AC433DB 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 DA47264E5F for ; Sat, 20 Feb 2021 01:34:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229867AbhBTBe2 (ORCPT ); Fri, 19 Feb 2021 20:34:28 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44170 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229678AbhBTBe1 (ORCPT ); Fri, 19 Feb 2021 20:34:27 -0500 Received: from mail-qv1-xf49.google.com (mail-qv1-xf49.google.com [IPv6:2607:f8b0:4864:20::f49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AEDEEC061793 for ; Fri, 19 Feb 2021 17:33:09 -0800 (PST) Received: by mail-qv1-xf49.google.com with SMTP id m1so4329418qvp.0 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=EXluNe1qjrCUk+CZIugU6E0ojfwb/lih3cvdCwmfR/E5/h/DRhV+h6grwMzwb2k2MA sOMqWTFEyyJnyxOmC6lt5sSD/yy4e6e1J6gRPUjq0uC1BtAqMjfu47WhTAayLUczdy4m pqWc/k1/dv/SSbDLF7KWIlNpN7UeXp7sISxniLzMYaUHeBjCpgVjG7LZphlNYtgIq4fV Gygh/DB3ZvcObbWwjszFE4OD8cHkZBvMpR6V3Z1GgjzLhWjCfefBpOyoydXqZWi4VTcq wXKN9hCJeIEH259o2msaRXabn7A51Mf2nwupk21OS055TUKZCq45JM0HXspyoFnFt16v CmNg== X-Gm-Message-State: AOAM532b+IbOcvOTJTF3MIc7jAmhnnpILpavAe7IpVWRWS0rnGCoBclr eib8gssiu5IoAoe11zVa5OKfrY1/VAFinPyxU8pykQ== 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-integrity@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: 12096491 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 E3573C43331 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 ADCF064EE3 for ; Sat, 20 Feb 2021 01:34:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229828AbhBTBeb (ORCPT ); Fri, 19 Feb 2021 20:34:31 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44182 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229876AbhBTBe2 (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 6D8BCC0617AB for ; Fri, 19 Feb 2021 17:33:10 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id f81so8787627yba.8 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=DBtjztcd0lnVJ9fIBdrvFvcBaGpiH0iEbJNDtfji+gqqRND8weFvhLSlZZbxojbKGi 3rRZ88SvZ/zgY8yQVNlEWSWzwP1PCfyxewyAS+0iEA62Cy266R/WSqbdYi20Wq8szcHo EYB6N4Rpm1cHonv31pkw8HhAcwHnZgRMuRRxwkwM4uSAprjUkihvbiWaXprngrbpEOUi 7ll1HUDoqLRLz2cTNU8m092g5widq/Z10V8c/rBS4WfGesgZjVvGMZNl7jA/AISTBEAn t6ZHiervU6YclEfyF+OBwd1CbFDO75yHkdiJXh1PPx8rlz28vOOkoqgTtYxOOlu8ivfW hqQQ== X-Gm-Message-State: AOAM531zG49lEXbgeATBoIg2AQjXab8SBQrh9St9xcX/xYf8o9VJpFKp xx8yUFjTbs47lUjXNnJm8JQezBzP5pgITB9miDlqwA== 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-integrity@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: 12096489 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 90E6EC4332D 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 6A1C164E86 for ; Sat, 20 Feb 2021 01:34:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229908AbhBTBec (ORCPT ); Fri, 19 Feb 2021 20:34:32 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44202 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229891AbhBTBeb (ORCPT ); Fri, 19 Feb 2021 20:34:31 -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 E4796C06121D for ; Fri, 19 Feb 2021 17:33:11 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id f81so8787686yba.8 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=ZBFaj5kKdluvAKBiieUvW6OrFhqbdHHlUPT01B2LhAkLyfALi5zOgLAZjXTPzeTBFE CPLAie7Cq/g+5xHh9tdVt+CnNeqty89wu4cIwKRBbGZNgD2VPavLCf85Xwdjn6zhA3xN hVnu0WTIAoKnsYH4YMezKvgzJuWs3UuPNVI3ymLMGiiMrmpZY3hqZnIyzNeb1SlEAKBL 44jmSSppVfrJFiyOqGHxL4zzrYP5mvJBqlfbXRNBpdGWLUiwa8wZ1IAlD58rUOerBwqP XT1/Gf5VTvGgMXKasz2SYCGMYjKegnGRxiKT9+Cf6gmTWAJrtBsrljC4+89LbPvJ7k65 oSVg== X-Gm-Message-State: AOAM531Vmi33ub+LiJiW2MtRBnHT0cDUthNduf7z/KDjz0Ky5owVO7/M BA75wjE0P3XlMArqDKQ9XfPnBrg87zvmhu/SH+9nEA== 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-integrity@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: 12096493 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 77211C433DB 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 292AE64E76 for ; Sat, 20 Feb 2021 01:35:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229953AbhBTBeu (ORCPT ); Fri, 19 Feb 2021 20:34:50 -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 S229931AbhBTBen (ORCPT ); Fri, 19 Feb 2021 20:34:43 -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 7F095C061221 for ; Fri, 19 Feb 2021 17:33:13 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id 194so8793845ybl.5 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=FZGFwQlN9Hfx9/yg3soTnPKr3WOQaQqzbQwBuOKRcZEoTjQMJWZ22cutf6VHu0OPSK rk3lFOAAGmdgpOcC0Fks3XR+qTXwtPeZDl+ddshoWp6y1Y11yycZ5Dp0KA8VyvNdTbze zwN1YfrV1NY/VdfCMvs4Mqs7K2dSOdSxBr227fSulguVuEkYJmUQ8ima+KR80z9PySNC 1A6+djJaKxQK0jjKNJsl/1k0UXqPhwGeOq/ufAYCd3eIal737fkvOCfNavGbcR0nh7sO UPS9EZ9yI2UEfPdu1FHTf85Sokiu3C/HuttQB6ffN9CGfTZ+k0bxNDDQJLiWO/lMYP03 CuqA== X-Gm-Message-State: AOAM533f8SSEfMEKl7p2Wf46diIeiqcOSI3U1NMRoNxf81Va4OOBno9R wvPDyv164HbI8uRrdR8NlS/Nyj3hMq9+X3SXfKTN2A== 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-integrity@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: 12096503 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 A0AF8C433E6 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 63E8E64EDE for ; Sat, 20 Feb 2021 01:35:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230016AbhBTBfe (ORCPT ); Fri, 19 Feb 2021 20:35:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44342 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230015AbhBTBfJ (ORCPT ); Fri, 19 Feb 2021 20:35:09 -0500 Received: from mail-qv1-xf4a.google.com (mail-qv1-xf4a.google.com [IPv6:2607:f8b0:4864:20::f4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0951AC061226 for ; Fri, 19 Feb 2021 17:33:15 -0800 (PST) Received: by mail-qv1-xf4a.google.com with SMTP id p4so4306914qvn.23 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=o/3EIAcvtlHdmCN5pZ4T0aeX3PQgApM7pevp+mfIyVx3ZLxDQjEHBggDkw4Up0MuYK 4T3xt9XqRNPCw+V5BSV4DQir20g9ac97pP8LPojYOyrf3QzedaQmAYjo+EFwpC3TFCg0 MioDb6z+Xz3azZVcFURDcwdy4m/XipMK+oNBtXK1RHfosydKsFiCYYEeskKVBtVa4sbC 0bbH0cryqJIVuBYI7n0vwNjmCPRNam7m406dE1ET+e103z7NlChEtJy+u0um5IwcpwXW S0oxZuQALNLccGqTuV4axJ/5Yhb/I3uDh8fbPp5Wi83LuIpNrFyIsJm4DmRrq7GNDEw1 ymcw== X-Gm-Message-State: AOAM531SY4HGgdEfMNM1v1ShlZb/uGyhdvISrk1mKfUFd7NImHj4st2y 4DHE/ZryX+N47W12sElmhaQGBrklHIj4+g1bx3RCVg== 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-integrity@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: 12096501 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 63609C433DB 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 2626564EB4 for ; Sat, 20 Feb 2021 01:35:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230048AbhBTBfb (ORCPT ); Fri, 19 Feb 2021 20:35:31 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44346 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230016AbhBTBfK (ORCPT ); Fri, 19 Feb 2021 20:35:10 -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 DF467C0611C1 for ; Fri, 19 Feb 2021 17:33:16 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id u1so8817932ybu.14 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=XJYc2uM4K/SGlp/Z7esky7V9+Zsvo3rz19qaTSNLNQoRiDufvZLKzULeI6BQqC3UcI dBkUVEPTAmsqj51kfUli44OSKcHLbYk/HLaZ3hhGbM7qHQSqvWLNmsp6o28zI66ON94M 3mjErDzpoys3gihcXFQ2xY0mcP3kqavSuJ9hwuApdTf7slxdQD9rSPFwoDbOrycUYqnX Z9BWgenQ99lc4Zyb8qDVZZWmkbENxCgDi0x+biTnvjE29pCTDsLM21imGRFzxz49VfDS bnqCDfNVuBmlk/BxVKUpvpi5QafLg0ddCJb9BIJQcFT4G5oEg6DKod+TO9kpR7Vn438V GDFw== X-Gm-Message-State: AOAM533XXbCm3ZtX5ZaM8TgIGLJTc2DARN6dqmCd9mJOSkwJxktsz6w/ n9gegQbxVm5VFErNjNn7X1W1peT+B8eqoLdETzoyuA== 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-integrity@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: 12096497 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 F0D77C4332B 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 C0F6864EE2 for ; Sat, 20 Feb 2021 01:35:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229985AbhBTBfA (ORCPT ); Fri, 19 Feb 2021 20:35:00 -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 S229945AbhBTBer (ORCPT ); Fri, 19 Feb 2021 20:34:47 -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 A95BAC0611BE for ; Fri, 19 Feb 2021 17:33:18 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id q77so5455392ybq.0 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=Bb8qA265xT6Jq5YD5u+EHqqNNfPoCBPWn6879cE+3nQN2jqx6x1KM3ZDnZs7oB+nV1 XbsX6rDyvuVOn4p+G1LKx/iG1boCVoEZS7EA+xGaA1aGkkeDhUSfipnDcuulSyThH3D9 3vDELT04ByiDGuAKnuNcHN0FlGey80J3l5aSWq6XLxrjnTadT6NsnSL5AF8ofMG5C+9o mFEE51+6SxxfEcAHT8LNqB4xkuL1rk8F3eoOIRtQqR6+Ne6Y/GPMVD/1le5kw+k82PSg 9NhBighXhrU48Ep+46HbY/pPwpP0kI0Hj3eRFYAOT9FOEDVAG/Qp0/CJvu7BMobpkxeu ds6g== X-Gm-Message-State: AOAM532iS+C14TQ3EzlXzl3WzGKMJg0uenUxp5IIuWzb0KV9nlWFksLO HIZNTQATziV8YHl2tjPtsICg7HfCplk4XHpqBRe7OQ== 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-integrity@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: 12096495 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 1477DC433E0 for ; Sat, 20 Feb 2021 01:35:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E522964EDF for ; Sat, 20 Feb 2021 01:35:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229945AbhBTBfB (ORCPT ); Fri, 19 Feb 2021 20:35:01 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44200 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229947AbhBTBer (ORCPT ); Fri, 19 Feb 2021 20:34:47 -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 316BFC061A2A for ; Fri, 19 Feb 2021 17:33:20 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id o9so8711613yba.18 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=SbqYx4eJGOpmnUWnrk4s1jJ1SNziKFxrkGKY67mS7s9ywwcLG+3ioxuQkZwWQvMDY8 gIwlbrseWDkl421y15zUzUb5IX10vQCvD+2V0CtbiKiwK4yzcpjzCMJRuuaLAOoxzD7V zXKefaUtqGSgHQELla552Q4HjG+p4Y+gFHEgnQW02blobvLtuN35Zsu9XqTwGgY0J4oc WpCIDaj5TFSvwuFkXD6vdDiJvjVd93J2rEhTRycDzcEDCu7/qJdGFiY99iK/HwmBSk35 eSHHSicrof13q8832GTSa1U35oYeUOm6K7c0DOIrqC8F+Lj4+qTGrxyqq6nem0lAuGKj kkKg== X-Gm-Message-State: AOAM530mEXbugDVS3/KJw9vndheVRSW7ptYPV/2uoZ2bKNZEsVX3+N3m ou9lXMLK5b8R+NmMKGwGAM/HEnQGGSGs326DsM7Zbw== 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-integrity@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: 12096499 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 37044C433E9 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 0359064EEC for ; Sat, 20 Feb 2021 01:35:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229996AbhBTBfQ (ORCPT ); Fri, 19 Feb 2021 20:35:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44174 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230000AbhBTBfF (ORCPT ); Fri, 19 Feb 2021 20:35:05 -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 DD812C061A30 for ; Fri, 19 Feb 2021 17:33:21 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id 194so8794103ybl.5 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=JL41lboZg60lw7FIdtQKdTWpAlp4zmSimOEV+tRHwRu705CpOMkbFVBch+WtboXixd PQNbf/Z1Kg76b60fyCPnKW9Q4pAp0RB0pLfy2qlCo32f+WzqWBHVQl3QLWR4C8Q3hZNw b1kGSE+mLUIZGGV6iZpLv54BfRimXAHiFtUcWlYR9OJrIeEGsWeYuatbjoMLM/inSC0J qQBn9YNB8c5C0yBPI1XEW9OAKrOdpO1FrwxLpLZS1IrLd+ZEdiP74gGRlonfPRagOgdH 32si+Q4hrTyC9L6dSYfl6YUpklSaalGodXXJWxsTGVixHIVG1jzSfaid1F3ixKmeRKCH EzqA== X-Gm-Message-State: AOAM532FG+wITsefhb3QEHvgQew6g23Lumq9MdyRVMMepcxinx2C2FbG dEa2iU8ioZfOsFjf7bP0MCRffpP8IR8MT77d8D7gyw== 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-integrity@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;