forked from pool/grub2
53da76e569
- Update to the latest upstreaming TPM2 patches * 0001-key_protector-Add-key-protectors-framework.patch - Replace 0001-protectors-Add-key-protectors-framework.patch * 0002-tpm2-Add-TPM-Software-Stack-TSS.patch - Merge other TSS patches * 0001-tpm2-Add-TPM2-types-structures-and-command-constants.patch * 0002-tpm2-Add-more-marshal-unmarshal-functions.patch * 0003-tpm2-Implement-more-TPM2-commands.patch * 0003-key_protector-Add-TPM2-Key-Protector.patch - Replace 0003-protectors-Add-TPM2-Key-Protector.patch * 0004-cryptodisk-Support-key-protectors.patch * 0005-util-grub-protect-Add-new-tool.patch * 0001-tpm2-Support-authorized-policy.patch - Replace 0004-tpm2-Support-authorized-policy.patch * 0001-tpm2-Add-extra-RSA-SRK-types.patch * 0001-tpm2-Implement-NV-index.patch - Replace 0001-protectors-Implement-NV-index.patch * 0002-cryptodisk-Fallback-to-passphrase.patch * 0003-cryptodisk-wipe-out-the-cached-keys-from-protectors.patch * 0004-diskfilter-look-up-cryptodisk-devices-first.patch - Refresh affected patches * 0001-Improve-TPM-key-protection-on-boot-interruptions.patch * grub2-bsc1220338-key_protector-implement-the-blocklist.patch - New manpage for grub2-protect OBS-URL: https://build.opensuse.org/request/show/1174325 OBS-URL: https://build.opensuse.org/package/show/Base:System/grub2?expand=0&rev=504
2264 lines
66 KiB
Diff
2264 lines
66 KiB
Diff
From 2cb2b00028ca7f43fc069472fbad7b9f129ec24b Mon Sep 17 00:00:00 2001
|
|
From: Hernan Gatta <hegatta@linux.microsoft.com>
|
|
Date: Tue, 1 Feb 2022 05:02:55 -0800
|
|
Subject: [PATCH 3/5] key_protector: Add TPM2 Key Protector
|
|
|
|
The TPM2 key protector is a module that enables the automatic retrieval
|
|
of a fully-encrypted disk's unlocking key from a TPM 2.0.
|
|
|
|
The theory of operation is such that the module accepts various
|
|
arguments, most of which are optional and therefore possess reasonable
|
|
defaults. One of these arguments is the keyfile/tpm2key parameter, which
|
|
is mandatory. There are two supported key formats:
|
|
|
|
1. Raw Sealed Key (--keyfile)
|
|
When sealing a key with TPM2_Create, the public portion of the sealed
|
|
key is stored in TPM2B_PUBLIC, and the private portion is in
|
|
TPM2B_PRIVATE. The raw sealed key glues the fully marshalled
|
|
TPM2B_PUBLIC and TPM2B_PRIVATE into one file.
|
|
|
|
2. TPM 2.0 Key (--tpm2key)
|
|
The following is the ASN.1 definition of TPM 2.0 Key File:
|
|
|
|
TPMPolicy ::= SEQUENCE {
|
|
CommandCode [0] EXPLICIT INTEGER
|
|
CommandPolicy [1] EXPLICIT OCTET STRING
|
|
}
|
|
|
|
TPMAuthPolicy ::= SEQUENCE {
|
|
Name [0] EXPLICIT UTF8STRING OPTIONAL
|
|
Policy [1] EXPLICIT SEQUENCE OF TPMPolicy
|
|
}
|
|
|
|
TPMKey ::= SEQUENCE {
|
|
type OBJECT IDENTIFIER
|
|
emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL
|
|
policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL
|
|
secret [2] EXPLICIT OCTET STRING OPTIONAL
|
|
authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL
|
|
description [4] EXPLICIT UTF8String OPTIONAL,
|
|
rsaParent [5] EXPLICIT BOOLEAN OPTIONAL,
|
|
parent INTEGER
|
|
pubkey OCTET STRING
|
|
privkey OCTET STRING
|
|
}
|
|
|
|
The TPM2 key protector only expects a "sealed" key in DER encoding,
|
|
so 'type' is always 2.23.133.10.1.5, 'emptyAuth' is 'TRUE', and
|
|
'secret' is empty. 'policy' and 'authPolicy' are the possible policy
|
|
command sequences to construst the policy digest to unseal the key.
|
|
Similar to the raw sealed key, the public portion (TPM2B_PUBLIC) of
|
|
the sealed key is stored in 'pubkey', and the private portion
|
|
(TPM2B_PRIVATE) is in 'privkey'.
|
|
|
|
For more details: https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
|
|
|
|
This sealed key file is created via the grub-protect tool. The tool
|
|
utilizes the TPM's sealing functionality to seal (i.e., encrypt) an
|
|
unlocking key using a Storage Root Key (SRK) to the values of various
|
|
Platform Configuration Registers (PCRs). These PCRs reflect the state
|
|
of the system as it boots. If the values are as expected, the system
|
|
may be considered trustworthy, at which point the TPM allows for a
|
|
caller to utilize the private component of the SRK to unseal (i.e.,
|
|
decrypt) the sealed key file. The caller, in this case, is this key
|
|
protector.
|
|
|
|
The TPM2 key protector registers two commands:
|
|
|
|
- tpm2_key_protector_init: Initializes the state of the TPM2 key
|
|
protector for later usage, clearing any
|
|
previous state, too, if any.
|
|
|
|
- tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init.
|
|
|
|
The way this is expected to be used requires the user to, either
|
|
interactively or, normally, via a boot script, initialize/configure
|
|
the key protector and then specify that it be used by the 'cryptomount'
|
|
command (modifications to this command are in a different patch).
|
|
|
|
For instance, to unseal the raw sealed key file:
|
|
|
|
tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub2/sealed-1.key
|
|
cryptomount -u <PART1_UUID> -P tpm2
|
|
|
|
tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub2/sealed-2.key --pcrs=7,11
|
|
cryptomount -u <PART2_UUID> -P tpm2
|
|
|
|
Or, to unseal the TPM 2.0 Key file:
|
|
|
|
tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-1.tpm
|
|
cryptomount -u <PART1_UUID> -P tpm2
|
|
|
|
tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-2.tpm --pcrs=7,11
|
|
cryptomount -u <PART2_UUID> -P tpm2
|
|
|
|
If a user does not initialize the key protector and attempts to use it
|
|
anyway, the protector returns an error.
|
|
|
|
Before unsealing the key, the TPM2 key protector follows the "TPMPolicy"
|
|
sequences to enforce the TPM policy commands to construct a valid policy
|
|
digest to unseal the key.
|
|
|
|
For the TPM 2.0 Key files, 'authPolicy' may contain multiple "TPMPolicy"
|
|
sequences, the TPM2 key protector iterates 'authPolicy' to find a valid
|
|
sequence to unseal key. If 'authPolicy' is empty or all sequences in
|
|
'authPolicy' fail, the protector tries the one from 'policy'. In case
|
|
'policy' is also empty, the protector creates a "TPMPolicy" sequence
|
|
based on the given PCR selection.
|
|
|
|
For the raw sealed key, the TPM2 key protector treats the key file as a
|
|
TPM 2.0 Key file without 'authPolicy' and 'policy', so the "TPMPolicy"
|
|
sequence is always based on the PCR selection from the command
|
|
parameters.
|
|
|
|
This commit only supports one policy command: TPM2_PolicyPCR. The
|
|
command set will be extended to support advanced features, such as
|
|
authorized policy, in the later commits.
|
|
|
|
Cc: Stefan Berger <stefanb@linux.ibm.com>
|
|
Cc: James Bottomley <jejb@linux.ibm.com>
|
|
Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
|
|
Signed-off-by: Gary Lin <glin@suse.com>
|
|
---
|
|
grub-core/Makefile.core.def | 13 +
|
|
grub-core/tpm2/args.c | 140 ++++
|
|
grub-core/tpm2/module.c | 1226 +++++++++++++++++++++++++++++
|
|
grub-core/tpm2/tpm2key.asn | 33 +
|
|
grub-core/tpm2/tpm2key.c | 476 +++++++++++
|
|
grub-core/tpm2/tpm2key_asn1_tab.c | 45 ++
|
|
include/grub/tpm2/internal/args.h | 49 ++
|
|
include/grub/tpm2/tpm2key.h | 86 ++
|
|
8 files changed, 2068 insertions(+)
|
|
create mode 100644 grub-core/tpm2/args.c
|
|
create mode 100644 grub-core/tpm2/module.c
|
|
create mode 100644 grub-core/tpm2/tpm2key.asn
|
|
create mode 100644 grub-core/tpm2/tpm2key.c
|
|
create mode 100644 grub-core/tpm2/tpm2key_asn1_tab.c
|
|
create mode 100644 include/grub/tpm2/internal/args.h
|
|
create mode 100644 include/grub/tpm2/tpm2key.h
|
|
|
|
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
|
|
index 4307b8e2d..a3c728442 100644
|
|
--- a/grub-core/Makefile.core.def
|
|
+++ b/grub-core/Makefile.core.def
|
|
@@ -2612,6 +2612,19 @@ module = {
|
|
enable = efi;
|
|
};
|
|
|
|
+module = {
|
|
+ name = tpm2;
|
|
+ common = tpm2/args.c;
|
|
+ common = tpm2/buffer.c;
|
|
+ common = tpm2/module.c;
|
|
+ common = tpm2/mu.c;
|
|
+ common = tpm2/tpm2.c;
|
|
+ common = tpm2/tpm2key.c;
|
|
+ common = tpm2/tpm2key_asn1_tab.c;
|
|
+ efi = tpm2/tcg2.c;
|
|
+ enable = efi;
|
|
+};
|
|
+
|
|
module = {
|
|
name = tr;
|
|
common = commands/tr.c;
|
|
diff --git a/grub-core/tpm2/args.c b/grub-core/tpm2/args.c
|
|
new file mode 100644
|
|
index 000000000..c11280ab9
|
|
--- /dev/null
|
|
+++ b/grub-core/tpm2/args.c
|
|
@@ -0,0 +1,140 @@
|
|
+/*
|
|
+ * GRUB -- GRand Unified Bootloader
|
|
+ * Copyright (C) 2022 Microsoft Corporation
|
|
+ *
|
|
+ * GRUB is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * GRUB is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+#include <grub/err.h>
|
|
+#include <grub/mm.h>
|
|
+#include <grub/misc.h>
|
|
+#include <grub/tpm2/internal/args.h>
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs,
|
|
+ grub_uint8_t *pcr_count)
|
|
+{
|
|
+ char *current_pcr = value;
|
|
+ char *next_pcr;
|
|
+ unsigned long pcr;
|
|
+ grub_uint8_t i;
|
|
+
|
|
+ if (grub_strlen (value) == 0)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ *pcr_count = 0;
|
|
+ for (i = 0; i < TPM_MAX_PCRS; i++)
|
|
+ {
|
|
+ next_pcr = grub_strchr (current_pcr, ',');
|
|
+ if (next_pcr == current_pcr)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Empty entry in PCR list"));
|
|
+ if (next_pcr)
|
|
+ *next_pcr = '\0';
|
|
+
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ pcr = grub_strtoul (current_pcr, NULL, 10);
|
|
+ if (grub_errno != GRUB_ERR_NONE)
|
|
+ return grub_error (grub_errno,
|
|
+ N_("Entry '%s' in PCR list is not a number"),
|
|
+ current_pcr);
|
|
+
|
|
+ if (pcr > TPM_MAX_PCRS)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
+ N_("Entry %lu in PCR list is too large to be a PCR "
|
|
+ "number, PCR numbers range from 0 to %u"),
|
|
+ pcr, TPM_MAX_PCRS);
|
|
+
|
|
+ pcrs[i] = (grub_uint8_t)pcr;
|
|
+ *pcr_count += 1;
|
|
+
|
|
+ if (next_pcr == NULL)
|
|
+ break;
|
|
+
|
|
+ current_pcr = next_pcr + 1;
|
|
+ if (*current_pcr == '\0')
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Trailing comma at the end of PCR list"));
|
|
+ }
|
|
+
|
|
+ if (i == TPM_MAX_PCRS)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
+ N_("Too many PCRs in PCR list, the maximum number of "
|
|
+ "PCRs is %u"), TPM_MAX_PCRS);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2_protector_parse_asymmetric (const char *value,
|
|
+ grub_srk_type_t *srk_type)
|
|
+{
|
|
+ if (grub_strcasecmp (value, "ECC") == 0 ||
|
|
+ grub_strcasecmp (value, "ECC_NIST_P256") == 0)
|
|
+ {
|
|
+ srk_type->type = TPM_ALG_ECC;
|
|
+ srk_type->detail.ecc_curve = TPM_ECC_NIST_P256;
|
|
+ }
|
|
+ else if (grub_strcasecmp (value, "RSA") == 0 ||
|
|
+ grub_strcasecmp (value, "RSA2048") == 0)
|
|
+ {
|
|
+ srk_type->type = TPM_ALG_RSA;
|
|
+ srk_type->detail.rsa_bits = 2048;
|
|
+ }
|
|
+ else
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
+ N_("Value '%s' is not a valid asymmetric key type"),
|
|
+ value);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID *bank)
|
|
+{
|
|
+ if (grub_strcasecmp (value, "SHA1") == 0)
|
|
+ *bank = TPM_ALG_SHA1;
|
|
+ else if (grub_strcasecmp (value, "SHA256") == 0)
|
|
+ *bank = TPM_ALG_SHA256;
|
|
+ else if (grub_strcasecmp (value, "SHA384") == 0)
|
|
+ *bank = TPM_ALG_SHA384;
|
|
+ else if (grub_strcasecmp (value, "SHA512") == 0)
|
|
+ *bank = TPM_ALG_SHA512;
|
|
+ else
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
+ N_("Value '%s' is not a valid PCR bank"), value);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle)
|
|
+{
|
|
+ unsigned long num;
|
|
+
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ num = grub_strtoul (value, NULL, 0);
|
|
+ if (grub_errno != GRUB_ERR_NONE)
|
|
+ return grub_error (grub_errno, N_("TPM handle value '%s' is not a number"),
|
|
+ value);
|
|
+
|
|
+ if (num > GRUB_UINT_MAX)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
+ N_("Value %lu is too large to be a TPM handle, TPM "
|
|
+ "handles are unsigned 32-bit integers"), num);
|
|
+
|
|
+ *handle = (TPM_HANDLE)num;
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
|
|
new file mode 100644
|
|
index 000000000..3db25ceca
|
|
--- /dev/null
|
|
+++ b/grub-core/tpm2/module.c
|
|
@@ -0,0 +1,1226 @@
|
|
+/*
|
|
+ * GRUB -- GRand Unified Bootloader
|
|
+ * Copyright (C) 2022 Microsoft Corporation
|
|
+ *
|
|
+ * GRUB is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * GRUB is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+#include <grub/dl.h>
|
|
+#include <grub/extcmd.h>
|
|
+#include <grub/file.h>
|
|
+#include <grub/libtasn1.h>
|
|
+#include <grub/list.h>
|
|
+#include <grub/misc.h>
|
|
+#include <grub/mm.h>
|
|
+#include <grub/key_protector.h>
|
|
+#include <grub/tpm2/buffer.h>
|
|
+#include <grub/tpm2/internal/args.h>
|
|
+#include <grub/tpm2/internal/types.h>
|
|
+#include <grub/tpm2/mu.h>
|
|
+#include <grub/tpm2/tpm2.h>
|
|
+#include <grub/tpm2/tpm2key.h>
|
|
+
|
|
+GRUB_MOD_LICENSE ("GPLv3+");
|
|
+
|
|
+typedef enum grub_tpm2_protector_mode
|
|
+{
|
|
+ GRUB_TPM2_PROTECTOR_MODE_UNSET,
|
|
+ GRUB_TPM2_PROTECTOR_MODE_SRK,
|
|
+ GRUB_TPM2_PROTECTOR_MODE_NV
|
|
+} grub_tpm2_protector_mode_t;
|
|
+
|
|
+enum grub_tpm2_protector_options
|
|
+{
|
|
+ OPTION_MODE,
|
|
+ OPTION_PCRS,
|
|
+ OPTION_BANK,
|
|
+ OPTION_TPM2KEY,
|
|
+ OPTION_KEYFILE,
|
|
+ OPTION_SRK,
|
|
+ OPTION_ASYMMETRIC,
|
|
+ OPTION_NVINDEX
|
|
+};
|
|
+
|
|
+struct grub_tpm2_protector_context
|
|
+{
|
|
+ grub_tpm2_protector_mode_t mode;
|
|
+ grub_uint8_t pcrs[TPM_MAX_PCRS];
|
|
+ grub_uint8_t pcr_count;
|
|
+ grub_srk_type_t srk_type;
|
|
+ TPM_ALG_ID bank;
|
|
+ const char *tpm2key;
|
|
+ const char *keyfile;
|
|
+ TPM_HANDLE srk;
|
|
+ TPM_HANDLE nv;
|
|
+};
|
|
+
|
|
+static const struct grub_arg_option grub_tpm2_protector_init_cmd_options[] =
|
|
+ {
|
|
+ /* Options for all modes */
|
|
+ {
|
|
+ .longarg = "mode",
|
|
+ .shortarg = 'm',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("Unseal key using SRK ('srk') (default) or retrieve it from an NV "
|
|
+ "Index ('nv')."),
|
|
+ },
|
|
+ {
|
|
+ .longarg = "pcrs",
|
|
+ .shortarg = 'p',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("Comma-separated list of PCRs used to authorize key release "
|
|
+ "e.g., '7,11'. (default: 7)"),
|
|
+ },
|
|
+ {
|
|
+ .longarg = "bank",
|
|
+ .shortarg = 'b',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("Bank of PCRs used to authorize key release: "
|
|
+ "SHA1, SHA256, SHA384 or SHA512. (default: SHA256)"),
|
|
+ },
|
|
+ /* SRK-mode options */
|
|
+ {
|
|
+ .longarg = "tpm2key",
|
|
+ .shortarg = 'T',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("In SRK mode, path to the key file in the TPM 2.0 Key File format "
|
|
+ "to unseal using the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed.tpm)."),
|
|
+ },
|
|
+ {
|
|
+ .longarg = "keyfile",
|
|
+ .shortarg = 'k',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("In SRK mode, path to the key file in the raw format to unseal "
|
|
+ "using the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed.key). "
|
|
+ "(Mainly for backward compatibility. Please use '--tpm2key'.)"),
|
|
+ },
|
|
+ {
|
|
+ .longarg = "srk",
|
|
+ .shortarg = 's',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("In SRK mode, the SRK handle if the SRK is persistent."),
|
|
+ },
|
|
+ {
|
|
+ .longarg = "asymmetric",
|
|
+ .shortarg = 'a',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("In SRK mode, the type of SRK: RSA (RSA2048) and ECC (ECC_NIST_P256)"
|
|
+ "(default: ECC)"),
|
|
+ },
|
|
+ /* NV Index-mode options */
|
|
+ {
|
|
+ .longarg = "nvindex",
|
|
+ .shortarg = 'n',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("Required in NV Index mode, the NV handle to read which must "
|
|
+ "readily exist on the TPM and which contains the key."),
|
|
+ },
|
|
+ /* End of list */
|
|
+ {0, 0, 0, 0, 0, 0}
|
|
+ };
|
|
+
|
|
+static grub_extcmd_t grub_tpm2_protector_init_cmd;
|
|
+static grub_extcmd_t grub_tpm2_protector_clear_cmd;
|
|
+static struct grub_tpm2_protector_context grub_tpm2_protector_ctx = { 0 };
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_srk_read_file (const char *filepath, void **buffer,
|
|
+ grub_size_t *buffer_size)
|
|
+{
|
|
+ grub_file_t file;
|
|
+ grub_off_t file_size;
|
|
+ void *read_buffer;
|
|
+ grub_off_t read_n;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /* Using GRUB_FILE_TYPE_SIGNATURE ensures we do not hash the keyfile into PCR9
|
|
+ * otherwise we'll never be able to predict the value of PCR9 at unseal time */
|
|
+ file = grub_file_open (filepath, GRUB_FILE_TYPE_SIGNATURE);
|
|
+ if (file == NULL)
|
|
+ {
|
|
+ /* Push errno from grub_file_open() into the error message stack */
|
|
+ grub_error_push();
|
|
+ err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
|
|
+ N_("Could not open file: %s\n"),
|
|
+ filepath);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ file_size = grub_file_size (file);
|
|
+ if (file_size == 0)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
+ N_("Could not read file size: %s"),
|
|
+ filepath);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ read_buffer = grub_malloc (file_size);
|
|
+ if (read_buffer == NULL)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
+ N_("Could not allocate buffer for %s"),
|
|
+ filepath);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ read_n = grub_file_read (file, read_buffer, file_size);
|
|
+ if (read_n != file_size)
|
|
+ {
|
|
+ grub_free (read_buffer);
|
|
+ err = grub_error (GRUB_ERR_FILE_READ_ERROR,
|
|
+ N_("Could not retrieve file contents: %s"),
|
|
+ filepath);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ *buffer = read_buffer;
|
|
+ *buffer_size = file_size;
|
|
+
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+error:
|
|
+ if (file != NULL)
|
|
+ grub_file_close (file);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_srk_unmarshal_keyfile (void *sealed_key,
|
|
+ grub_size_t sealed_key_size,
|
|
+ TPM2_SEALED_KEY *sk)
|
|
+{
|
|
+ struct grub_tpm2_buffer buf;
|
|
+
|
|
+ grub_tpm2_buffer_init (&buf);
|
|
+ if (sealed_key_size > buf.cap)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Sealed key larger than %" PRIuGRUB_SIZE " bytes"),
|
|
+ buf.cap);
|
|
+
|
|
+ grub_memcpy (buf.data, sealed_key, sealed_key_size);
|
|
+ buf.size = sealed_key_size;
|
|
+
|
|
+ grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
|
|
+ grub_tpm2_mu_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private);
|
|
+
|
|
+ if (buf.error)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Malformed TPM wire key file"));
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_srk_unmarshal_tpm2key (void *sealed_key,
|
|
+ grub_size_t sealed_key_size,
|
|
+ tpm2key_policy_t *policy_seq,
|
|
+ tpm2key_authpolicy_t *authpol_seq,
|
|
+ grub_uint8_t *rsaparent,
|
|
+ grub_uint32_t *parent,
|
|
+ TPM2_SEALED_KEY *sk)
|
|
+{
|
|
+ asn1_node tpm2key = NULL;
|
|
+ grub_uint8_t rsaparent_tmp;
|
|
+ grub_uint32_t parent_tmp;
|
|
+ void *sealed_pub = NULL;
|
|
+ grub_size_t sealed_pub_size;
|
|
+ void *sealed_priv = NULL;
|
|
+ grub_size_t sealed_priv_size;
|
|
+ struct grub_tpm2_buffer buf;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /*
|
|
+ * Start to parse the tpm2key file
|
|
+ * TPMKey ::= SEQUENCE {
|
|
+ * type OBJECT IDENTIFIER,
|
|
+ * emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
|
|
+ * policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
|
|
+ * secret [2] EXPLICIT OCTET STRING OPTIONAL,
|
|
+ * authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
|
|
+ * description [4] EXPLICIT UTF8String OPTIONAL,
|
|
+ * rsaParent [5] EXPLICIT BOOLEAN OPTIONAL,
|
|
+ * parent INTEGER,
|
|
+ * pubkey OCTET STRING,
|
|
+ * privkey OCTET STRING
|
|
+ * }
|
|
+ */
|
|
+ err = grub_tpm2key_start_parsing (&tpm2key, sealed_key, sealed_key_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+
|
|
+ /*
|
|
+ * Retrieve the policy sequence from 'policy'
|
|
+ * policy_seq will be NULL when 'policy' is not available
|
|
+ */
|
|
+ err = grub_tpm2key_get_policy_seq (tpm2key, policy_seq);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /*
|
|
+ * Retrieve the authpolicy sequence from 'authPolicy'
|
|
+ * authpol_seq will be NULL when 'authPolicy' is not available
|
|
+ */
|
|
+ err = grub_tpm2key_get_authpolicy_seq (tpm2key, authpol_seq);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /* Retrieve rsaParent */
|
|
+ err = grub_tpm2key_get_rsaparent (tpm2key, &rsaparent_tmp);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ *rsaparent = rsaparent_tmp;
|
|
+
|
|
+ /* Retrieve the parent handle */
|
|
+ err = grub_tpm2key_get_parent (tpm2key, &parent_tmp);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /* The parent handle should be either PERMANENT or PERSISTENT. */
|
|
+ if (!TPM_HT_IS_PERMANENT (parent_tmp) && !TPM_HT_IS_PERSISTENT (parent_tmp))
|
|
+ {
|
|
+ err = GRUB_ERR_OUT_OF_RANGE;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ *parent = parent_tmp;
|
|
+
|
|
+ /* Retrieve the public part of the sealed key */
|
|
+ err = grub_tpm2key_get_pubkey (tpm2key, &sealed_pub, &sealed_pub_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /* Retrieve the private part of the sealed key */
|
|
+ err = grub_tpm2key_get_privkey (tpm2key, &sealed_priv, &sealed_priv_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /* Unmarshal the sealed key */
|
|
+ grub_tpm2_buffer_init (&buf);
|
|
+ if (sealed_pub_size + sealed_priv_size > buf.cap)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Sealed key larger than %" PRIuGRUB_SIZE " bytes"),
|
|
+ buf.cap);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ grub_tpm2_buffer_pack (&buf, sealed_pub, sealed_pub_size);
|
|
+ grub_tpm2_buffer_pack (&buf, sealed_priv, sealed_priv_size);
|
|
+
|
|
+ buf.offset = 0;
|
|
+
|
|
+ grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
|
|
+ grub_tpm2_mu_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private);
|
|
+
|
|
+ if (buf.error)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Malformed TPM 2.0 key file"));
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+error:
|
|
+ /* End the parsing */
|
|
+ grub_tpm2key_end_parsing (tpm2key);
|
|
+ grub_free (sealed_pub);
|
|
+ grub_free (sealed_priv);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* Check if the SRK exists in the specified handle */
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_srk_check (const TPM_HANDLE srk_handle)
|
|
+{
|
|
+ TPM_RC rc;
|
|
+ TPM2B_PUBLIC public;
|
|
+
|
|
+ /* Find SRK */
|
|
+ rc = TPM2_ReadPublic (srk_handle, NULL, &public);
|
|
+ if (rc == TPM_RC_SUCCESS)
|
|
+ return GRUB_ERR_NONE;
|
|
+
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Failed to retrieve SRK from 0x%x (TPM2_ReadPublic: 0x%x)"),
|
|
+ srk_handle, rc);
|
|
+}
|
|
+
|
|
+/* Get the SRK with the template */
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_srk_get (const grub_srk_type_t srk_type,
|
|
+ const TPM_HANDLE parent,
|
|
+ TPM_HANDLE *srk_handle)
|
|
+{
|
|
+ TPM_RC rc;
|
|
+ TPMT_PUBLIC_PARMS parms = { 0 };
|
|
+ TPMS_AUTH_COMMAND authCommand = { 0 };
|
|
+ TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
|
|
+ TPM2B_PUBLIC inPublic = { 0 };
|
|
+ TPM2B_DATA outsideInfo = { 0 };
|
|
+ TPML_PCR_SELECTION creationPcr = { 0 };
|
|
+ TPM2B_PUBLIC outPublic = { 0 };
|
|
+ TPM2B_CREATION_DATA creationData = { 0 };
|
|
+ TPM2B_DIGEST creationHash = { 0 };
|
|
+ TPMT_TK_CREATION creationTicket = { 0 };
|
|
+ TPM2B_NAME srkName = { 0 };
|
|
+ TPM_HANDLE tmp_handle = 0;
|
|
+
|
|
+ inPublic.publicArea.type = srk_type.type;
|
|
+ inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
+ inPublic.publicArea.objectAttributes.restricted = 1;
|
|
+ inPublic.publicArea.objectAttributes.userWithAuth = 1;
|
|
+ inPublic.publicArea.objectAttributes.decrypt = 1;
|
|
+ inPublic.publicArea.objectAttributes.fixedTPM = 1;
|
|
+ inPublic.publicArea.objectAttributes.fixedParent = 1;
|
|
+ inPublic.publicArea.objectAttributes.sensitiveDataOrigin = 1;
|
|
+ inPublic.publicArea.objectAttributes.noDA = 1;
|
|
+
|
|
+ if (srk_type.type == TPM_ALG_RSA)
|
|
+ {
|
|
+ inPublic.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES;
|
|
+ inPublic.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128;
|
|
+ inPublic.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB;
|
|
+ inPublic.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
|
|
+ inPublic.publicArea.parameters.rsaDetail.keyBits = srk_type.detail.rsa_bits;
|
|
+ inPublic.publicArea.parameters.rsaDetail.exponent = 0;
|
|
+ }
|
|
+ else if (srk_type.type == TPM_ALG_ECC)
|
|
+ {
|
|
+ inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES;
|
|
+ inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128;
|
|
+ inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB;
|
|
+ inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
|
|
+ inPublic.publicArea.parameters.eccDetail.curveID = srk_type.detail.ecc_curve;
|
|
+ inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
|
|
+ }
|
|
+ else
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown SRK algorithm"));
|
|
+
|
|
+ /* Test the parameters before SRK generation */
|
|
+ parms.type = srk_type.type;
|
|
+ grub_memcpy (&parms.parameters, &inPublic.publicArea.parameters,
|
|
+ sizeof (TPMU_PUBLIC_PARMS));
|
|
+
|
|
+ rc = TPM2_TestParms (&parms, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Unsupported SRK template (TPM2_TestParms: 0x%x)"),
|
|
+ rc);
|
|
+
|
|
+ /* Create SRK */
|
|
+ authCommand.sessionHandle = TPM_RS_PW;
|
|
+ rc = TPM2_CreatePrimary (parent, &authCommand, &inSensitive, &inPublic,
|
|
+ &outsideInfo, &creationPcr, &tmp_handle, &outPublic,
|
|
+ &creationData, &creationHash, &creationTicket,
|
|
+ &srkName, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_DEVICE,
|
|
+ N_("Could not create SRK (TPM2_CreatePrimary: 0x%x)"),
|
|
+ rc);
|
|
+
|
|
+ *srk_handle = tmp_handle;
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+/* Load the SRK from the persistent handle or create one with a given type of
|
|
+ template, and then associate the sealed key with the SRK
|
|
+ Return values:
|
|
+ * GRUB_ERR_NONE: Everything is fine.
|
|
+ * GRUB_ERR_BAD_ARGUMENT: The SRK doesn't match. Try another one.
|
|
+ * Other: Something went wrong.
|
|
+*/
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_srk_load (const grub_srk_type_t srk_type,
|
|
+ const TPM2_SEALED_KEY *sealed_key,
|
|
+ const TPM_HANDLE parent,
|
|
+ TPM_HANDLE *sealed_handle,
|
|
+ TPM_HANDLE *srk_handle)
|
|
+{
|
|
+ TPMS_AUTH_COMMAND authCmd = { 0 };
|
|
+ TPM2B_NAME name = { 0 };
|
|
+ TPM_RC rc;
|
|
+ grub_err_t err;
|
|
+
|
|
+ if (srk_handle == NULL)
|
|
+ return GRUB_ERR_BUG;
|
|
+
|
|
+ if (*srk_handle != 0)
|
|
+ {
|
|
+ err = grub_tpm2_protector_srk_check (*srk_handle);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ err = grub_tpm2_protector_srk_get (srk_type, parent, srk_handle);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ /* Load the sealed key and associate it with the SRK */
|
|
+ authCmd.sessionHandle = TPM_RS_PW;
|
|
+ rc = TPM2_Load (*srk_handle, &authCmd, &sealed_key->private, &sealed_key->public,
|
|
+ sealed_handle, &name, NULL);
|
|
+ /* If TPM2_Load returns (TPM_RC_INTEGRITY | TPM_RC_P | TPM_RC_1), then it
|
|
+ implies the wrong SRK is used. */
|
|
+ if (rc == (TPM_RC_INTEGRITY | TPM_RC_P | TPM_RC_1))
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("SRK not matched"));
|
|
+ goto error;
|
|
+ }
|
|
+ else if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_DEVICE,
|
|
+ N_("Failed to load sealed key (TPM2_Load: 0x%x)"),
|
|
+ rc);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+
|
|
+error:
|
|
+ if (!TPM_HT_IS_PERSISTENT (*srk_handle))
|
|
+ TPM2_FlushContext (*srk_handle);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static const char *
|
|
+srk_type_to_name (grub_srk_type_t srk_type)
|
|
+{
|
|
+ if (srk_type.type == TPM_ALG_ECC)
|
|
+ {
|
|
+ switch (srk_type.detail.ecc_curve)
|
|
+ {
|
|
+ case TPM_ECC_NIST_P256:
|
|
+ return "ECC_NIST_P256";
|
|
+ }
|
|
+ }
|
|
+ else if (srk_type.type == TPM_ALG_RSA)
|
|
+ {
|
|
+ switch (srk_type.detail.rsa_bits)
|
|
+ {
|
|
+ case 2048:
|
|
+ return "RSA2048";
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return "Unknown";
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_load_key (const struct grub_tpm2_protector_context *ctx,
|
|
+ const TPM2_SEALED_KEY *sealed_key,
|
|
+ const TPM_HANDLE parent_handle,
|
|
+ TPM_HANDLE *sealed_handle,
|
|
+ TPM_HANDLE *srk_handle)
|
|
+{
|
|
+ grub_err_t err;
|
|
+ int i;
|
|
+ grub_srk_type_t fallback_srks[] = {
|
|
+ {
|
|
+ .type = TPM_ALG_ECC,
|
|
+ .detail.ecc_curve = TPM_ECC_NIST_P256,
|
|
+ },
|
|
+ {
|
|
+ .type = TPM_ALG_RSA,
|
|
+ .detail.rsa_bits = 2048,
|
|
+ },
|
|
+ {
|
|
+ .type = TPM_ALG_ERROR,
|
|
+ }
|
|
+ };
|
|
+
|
|
+ /* Try the given persistent SRK if exists */
|
|
+ if (*srk_handle != 0)
|
|
+ {
|
|
+ err = grub_tpm2_protector_srk_load (ctx->srk_type, sealed_key,
|
|
+ parent_handle, sealed_handle,
|
|
+ srk_handle);
|
|
+ if (err != GRUB_ERR_BAD_ARGUMENT)
|
|
+ return err;
|
|
+
|
|
+ grub_print_error ();
|
|
+ grub_printf_ (N_("Trying the specified SRK algorithm: %s\n"),
|
|
+ srk_type_to_name (ctx->srk_type));
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ *srk_handle = 0;
|
|
+ }
|
|
+
|
|
+ /* Try the specified algorithm for the SRK template */
|
|
+ if (*srk_handle == 0)
|
|
+ {
|
|
+ err = grub_tpm2_protector_srk_load (ctx->srk_type, sealed_key,
|
|
+ parent_handle, sealed_handle,
|
|
+ srk_handle);
|
|
+ if (err != GRUB_ERR_BAD_ARGUMENT)
|
|
+ return err;
|
|
+
|
|
+ grub_print_error ();
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ *srk_handle = 0;
|
|
+ }
|
|
+
|
|
+ /* Try all the fallback SRK templates */
|
|
+ for (i = 0; fallback_srks[i].type != TPM_ALG_ERROR; i++)
|
|
+ {
|
|
+ /* Skip the specified algorithm */
|
|
+ if (fallback_srks[i].type == ctx->srk_type.type &&
|
|
+ (fallback_srks[i].detail.rsa_bits == ctx->srk_type.detail.rsa_bits ||
|
|
+ fallback_srks[i].detail.ecc_curve == ctx->srk_type.detail.ecc_curve))
|
|
+ continue;
|
|
+
|
|
+ grub_printf_ (N_("Trying fallback %s template\n"),
|
|
+ srk_type_to_name (fallback_srks[i]));
|
|
+
|
|
+ *srk_handle = 0;
|
|
+
|
|
+ err = grub_tpm2_protector_srk_load (fallback_srks[i], sealed_key,
|
|
+ parent_handle, sealed_handle,
|
|
+ srk_handle);
|
|
+ if (err != GRUB_ERR_BAD_ARGUMENT)
|
|
+ return err;
|
|
+
|
|
+ grub_print_error ();
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION session,
|
|
+ struct grub_tpm2_buffer *cmd_buf)
|
|
+{
|
|
+ TPM2B_DIGEST pcr_digest;
|
|
+ TPML_PCR_SELECTION pcr_sel;
|
|
+ TPM_RC rc;
|
|
+
|
|
+ grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, &pcr_digest);
|
|
+ grub_tpm2_mu_TPML_PCR_SELECTION_Unmarshal (cmd_buf, &pcr_sel);
|
|
+ if (cmd_buf->error)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Failed to unmarshal CommandPolicy for TPM2_PolicyPCR"));
|
|
+
|
|
+ rc = TPM2_PolicyPCR (session, NULL, &pcr_digest, &pcr_sel, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_DEVICE,
|
|
+ N_("Failed to submit PCR policy (TPM2_PolicyPCR: 0x%x)"),
|
|
+ rc);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_enforce_policy (tpm2key_policy_t policy, TPMI_SH_AUTH_SESSION session)
|
|
+{
|
|
+ struct grub_tpm2_buffer buf;
|
|
+ grub_err_t err;
|
|
+
|
|
+ grub_tpm2_buffer_init (&buf);
|
|
+ if (policy->cmd_policy_len > buf.cap)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("CommandPolicy larger than TPM buffer"));
|
|
+
|
|
+ grub_memcpy (buf.data, policy->cmd_policy, policy->cmd_policy_len);
|
|
+ buf.size = policy->cmd_policy_len;
|
|
+
|
|
+ switch (policy->cmd_code)
|
|
+ {
|
|
+ case TPM_CC_PolicyPCR:
|
|
+ err = grub_tpm2_protector_policypcr (session, &buf);
|
|
+ break;
|
|
+ default:
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Unknown TPM Command: 0x%x"), policy->cmd_code);
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_enforce_policy_seq (tpm2key_policy_t policy_seq,
|
|
+ TPMI_SH_AUTH_SESSION session)
|
|
+{
|
|
+ tpm2key_policy_t policy;
|
|
+ grub_err_t err;
|
|
+
|
|
+ FOR_LIST_ELEMENTS (policy, policy_seq)
|
|
+ {
|
|
+ err = grub_tpm2_protector_enforce_policy (policy, session);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_simple_policy_seq (const struct grub_tpm2_protector_context *ctx,
|
|
+ tpm2key_policy_t *policy_seq)
|
|
+{
|
|
+ tpm2key_policy_t policy = NULL;
|
|
+ struct grub_tpm2_buffer buf;
|
|
+ TPML_PCR_SELECTION pcr_sel = {
|
|
+ .count = 1,
|
|
+ .pcrSelections = {
|
|
+ {
|
|
+ .hash = ctx->bank,
|
|
+ .sizeOfSelect = 3,
|
|
+ .pcrSelect = { 0 }
|
|
+ },
|
|
+ }
|
|
+ };
|
|
+ grub_uint8_t i;
|
|
+ grub_err_t err;
|
|
+
|
|
+ if (policy_seq == NULL)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ grub_tpm2_buffer_init (&buf);
|
|
+
|
|
+ for (i = 0; i < ctx->pcr_count; i++)
|
|
+ TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], ctx->pcrs[i]);
|
|
+
|
|
+ grub_tpm2_buffer_pack_u16 (&buf, 0);
|
|
+ grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&buf, &pcr_sel);
|
|
+
|
|
+ if (buf.error)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ policy = grub_malloc (sizeof(struct tpm2key_policy));
|
|
+ if (policy == NULL)
|
|
+ {
|
|
+ err = GRUB_ERR_OUT_OF_MEMORY;
|
|
+ goto error;
|
|
+ }
|
|
+ policy->cmd_code = TPM_CC_PolicyPCR;
|
|
+ policy->cmd_policy = grub_malloc (buf.size);
|
|
+ if (policy->cmd_policy == NULL)
|
|
+ {
|
|
+ err = GRUB_ERR_OUT_OF_MEMORY;
|
|
+ goto error;
|
|
+ }
|
|
+ grub_memcpy (policy->cmd_policy, buf.data, buf.size);
|
|
+ policy->cmd_policy_len = buf.size;
|
|
+
|
|
+ grub_list_push (GRUB_AS_LIST_P (policy_seq), GRUB_AS_LIST (policy));
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+
|
|
+error:
|
|
+ grub_free (policy);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE sealed_handle,
|
|
+ grub_uint8_t **key, grub_size_t *key_size)
|
|
+{
|
|
+ TPMS_AUTH_COMMAND authCmd = { 0 };
|
|
+ TPM2B_SENSITIVE_DATA data;
|
|
+ TPM2B_NONCE nonceCaller = { 0 };
|
|
+ TPMT_SYM_DEF symmetric = { 0 };
|
|
+ TPMI_SH_AUTH_SESSION session;
|
|
+ grub_uint8_t *key_out;
|
|
+ TPM_RC rc;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /* Start Auth Session */
|
|
+ nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
+ symmetric.algorithm = TPM_ALG_NULL;
|
|
+ rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, NULL, &nonceCaller, NULL,
|
|
+ TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256,
|
|
+ &session, NULL, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_DEVICE,
|
|
+ N_("Failed to start auth session (TPM2_StartAuthSession: 0x%x)"),
|
|
+ rc);
|
|
+
|
|
+ /* Enforce the policy command sequence */
|
|
+ err = grub_tpm2_protector_enforce_policy_seq (policy_seq, session);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /* Unseal Sealed Key */
|
|
+ authCmd.sessionHandle = session;
|
|
+ rc = TPM2_Unseal (sealed_handle, &authCmd, &data, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_DEVICE,
|
|
+ N_("Failed to unseal sealed key (TPM2_Unseal: 0x%x)"),
|
|
+ rc);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Epilogue */
|
|
+ key_out = grub_malloc (data.size);
|
|
+ if (key_out == NULL)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
+ N_("No memory left to allocate unlock key buffer"));
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ grub_memcpy (key_out, data.buffer, data.size);
|
|
+
|
|
+ *key = key_out;
|
|
+ *key_size = data.size;
|
|
+
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+error:
|
|
+ TPM2_FlushContext (session);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_srk_recover (const struct grub_tpm2_protector_context *ctx,
|
|
+ grub_uint8_t **key, grub_size_t *key_size)
|
|
+{
|
|
+ TPM2_SEALED_KEY sealed_key = { 0 };
|
|
+ void *file_bytes = NULL;
|
|
+ grub_size_t file_size = 0;
|
|
+ grub_uint8_t rsaparent = 0;
|
|
+ TPM_HANDLE parent_handle = 0;
|
|
+ TPM_HANDLE srk_handle = 0;
|
|
+ TPM_HANDLE sealed_handle = 0;
|
|
+ tpm2key_policy_t policy_seq = NULL;
|
|
+ tpm2key_authpolicy_t authpol = NULL;
|
|
+ tpm2key_authpolicy_t authpol_seq = NULL;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /*
|
|
+ * Retrieve sealed key, parent handle, policy sequence, and authpolicy
|
|
+ * sequence from the key file
|
|
+ */
|
|
+ if (ctx->tpm2key)
|
|
+ {
|
|
+ err = grub_tpm2_protector_srk_read_file (ctx->tpm2key, &file_bytes,
|
|
+ &file_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+
|
|
+ err = grub_tpm2_protector_srk_unmarshal_tpm2key (file_bytes,
|
|
+ file_size,
|
|
+ &policy_seq,
|
|
+ &authpol_seq,
|
|
+ &rsaparent,
|
|
+ &parent_handle,
|
|
+ &sealed_key);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto exit1;
|
|
+
|
|
+ if (rsaparent == 1)
|
|
+ {
|
|
+ struct grub_tpm2_protector_context *ctx_w;
|
|
+
|
|
+ /* Overwrite the SRK type as noted in the key */
|
|
+ ctx_w = (struct grub_tpm2_protector_context *)ctx;
|
|
+ ctx_w->srk_type.type = TPM_ALG_RSA;
|
|
+ ctx_w->srk_type.detail.rsa_bits = 2048;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ err = grub_tpm2_protector_srk_read_file (ctx->keyfile, &file_bytes,
|
|
+ &file_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+
|
|
+ parent_handle = TPM_RH_OWNER;
|
|
+ err = grub_tpm2_protector_srk_unmarshal_keyfile (file_bytes,
|
|
+ file_size,
|
|
+ &sealed_key);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto exit1;
|
|
+ }
|
|
+
|
|
+ /* Set the SRK handle if it is specified with '--srk' or inside the key file */
|
|
+ if (ctx->srk != 0)
|
|
+ srk_handle = ctx->srk;
|
|
+ else if (TPM_HT_IS_PERSISTENT (parent_handle))
|
|
+ srk_handle = parent_handle;
|
|
+
|
|
+ /* Load the sealed key into TPM and associate it with the SRK */
|
|
+ err = grub_tpm2_protector_load_key (ctx, &sealed_key, parent_handle,
|
|
+ &sealed_handle, &srk_handle);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto exit1;
|
|
+
|
|
+ /*
|
|
+ * Set err to an error code to trigger the standalone policy sequence
|
|
+ * if there is no authpolicy sequence
|
|
+ */
|
|
+ err = GRUB_ERR_READ_ERROR;
|
|
+
|
|
+ /* Iterate the authpolicy sequence to find one that unseals the key */
|
|
+ FOR_LIST_ELEMENTS (authpol, authpol_seq)
|
|
+ {
|
|
+ err = grub_tpm2_protector_unseal (authpol->policy_seq, sealed_handle,
|
|
+ key, key_size);
|
|
+ if (err == GRUB_ERR_NONE)
|
|
+ break;
|
|
+
|
|
+ /*
|
|
+ * Push the error message into the grub_error stack
|
|
+ * Note: The grub_error stack may overflow if there are too many policy
|
|
+ * sequences. Anyway, we still can keep the error messages from
|
|
+ * the first few policy sequences which are usually most likely to
|
|
+ * unseal the key.
|
|
+ */
|
|
+ grub_error_push();
|
|
+ }
|
|
+
|
|
+ /* Give the standalone policy sequence a try */
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ {
|
|
+ /*
|
|
+ * Create a basic policy sequence based on the given PCR selection if the
|
|
+ * key file doesn't provide one
|
|
+ */
|
|
+ if (policy_seq == NULL)
|
|
+ {
|
|
+ err = grub_tpm2_protector_simple_policy_seq (ctx, &policy_seq);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto exit2;
|
|
+ }
|
|
+
|
|
+ err = grub_tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size);
|
|
+ }
|
|
+
|
|
+ /* Pop error messages on success */
|
|
+ if (err == GRUB_ERR_NONE)
|
|
+ while (grub_error_pop ());
|
|
+
|
|
+exit2:
|
|
+ TPM2_FlushContext (sealed_handle);
|
|
+
|
|
+ if (!TPM_HT_IS_PERSISTENT (srk_handle))
|
|
+ TPM2_FlushContext (srk_handle);
|
|
+
|
|
+exit1:
|
|
+ grub_tpm2key_free_policy_seq (policy_seq);
|
|
+ grub_tpm2key_free_authpolicy_seq (authpol_seq);
|
|
+ grub_free (file_bytes);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx,
|
|
+ grub_uint8_t **key, grub_size_t *key_size)
|
|
+{
|
|
+ (void)ctx;
|
|
+ (void)key;
|
|
+ (void)key_size;
|
|
+
|
|
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
|
+ N_("NV Index mode is not implemented yet"));
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_recover (const struct grub_tpm2_protector_context *ctx,
|
|
+ grub_uint8_t **key, grub_size_t *key_size)
|
|
+{
|
|
+ switch (ctx->mode)
|
|
+ {
|
|
+ case GRUB_TPM2_PROTECTOR_MODE_SRK:
|
|
+ return grub_tpm2_protector_srk_recover (ctx, key, key_size);
|
|
+ case GRUB_TPM2_PROTECTOR_MODE_NV:
|
|
+ return grub_tpm2_protector_nv_recover (ctx, key, key_size);
|
|
+ default:
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+ }
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_recover_key (grub_uint8_t **key, grub_size_t *key_size)
|
|
+{
|
|
+ /* Expect a call to tpm2_protector_init before anybody tries to use us */
|
|
+ if (grub_tpm2_protector_ctx.mode == GRUB_TPM2_PROTECTOR_MODE_UNSET)
|
|
+ return grub_error (GRUB_ERR_INVALID_COMMAND,
|
|
+ N_("Cannot use TPM2 key protector without initializing "
|
|
+ "it, call tpm2_protector_init first"));
|
|
+
|
|
+ if (key == NULL || key_size == NULL)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ return grub_tpm2_protector_recover (&grub_tpm2_protector_ctx, key, key_size);
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
|
|
+{
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_UNSET)
|
|
+ ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
|
|
+
|
|
+ /* Checks for SRK mode */
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK &&
|
|
+ (ctx->keyfile == NULL && ctx->tpm2key == NULL))
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In SRK mode, a key file must be specified: "
|
|
+ "--tpm2key/-T or --keyfile/-k"));
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK &&
|
|
+ (ctx->keyfile != NULL && ctx->tpm2key != NULL))
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In SRK mode, please specify a key file with "
|
|
+ "only --tpm2key/-T or --keyfile/-k"));
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->nv != 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In SRK mode, an NV Index cannot be specified"));
|
|
+
|
|
+ /* Checks for NV mode */
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->nv == 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In NV Index mode, an NV Index must be specified: "
|
|
+ "--nvindex or -n"));
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV &&
|
|
+ (ctx->tpm2key != NULL || ctx->keyfile != NULL))
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In NV Index mode, a keyfile cannot be specified"));
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->srk != 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In NV Index mode, an SRK cannot be specified"));
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV &&
|
|
+ ctx->srk_type.type != TPM_ALG_ERROR)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In NV Index mode, an asymmetric key type cannot be "
|
|
+ "specified"));
|
|
+
|
|
+ /* Defaults assignment */
|
|
+ if (ctx->bank == TPM_ALG_ERROR)
|
|
+ ctx->bank = TPM_ALG_SHA256;
|
|
+
|
|
+ if (ctx->pcr_count == 0)
|
|
+ {
|
|
+ ctx->pcrs[0] = 7;
|
|
+ ctx->pcr_count = 1;
|
|
+ }
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK &&
|
|
+ ctx->srk_type.type == TPM_ALG_ERROR)
|
|
+ {
|
|
+ ctx->srk_type.type = TPM_ALG_ECC;
|
|
+ ctx->srk_type.detail.ecc_curve = TPM_ECC_NIST_P256;
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_parse_file (const char *value, const char **file)
|
|
+{
|
|
+ if (grub_strlen (value) == 0)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ *file = grub_strdup (value);
|
|
+ if (*file == NULL)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
+ N_("No memory to duplicate file path"));
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_parse_mode (const char *value,
|
|
+ grub_tpm2_protector_mode_t *mode)
|
|
+{
|
|
+ if (grub_strcmp (value, "srk") == 0)
|
|
+ *mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
|
|
+ else if (grub_strcmp (value, "nv") == 0)
|
|
+ *mode = GRUB_TPM2_PROTECTOR_MODE_NV;
|
|
+ else
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
+ N_("Value '%s' is not a valid TPM2 key protector mode"),
|
|
+ value);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc,
|
|
+ char **args __attribute__ ((unused)))
|
|
+{
|
|
+ struct grub_arg_list *state = ctxt->state;
|
|
+ grub_err_t err;
|
|
+
|
|
+ if (argc)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("The TPM2 key protector does not accept any "
|
|
+ "non-option arguments (i.e., like -o and/or --option "
|
|
+ "only)"));
|
|
+
|
|
+ grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
|
|
+ grub_memset (&grub_tpm2_protector_ctx, 0, sizeof (grub_tpm2_protector_ctx));
|
|
+
|
|
+ if (state[OPTION_MODE].set) /* mode */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_mode (state[OPTION_MODE].arg,
|
|
+ &grub_tpm2_protector_ctx.mode);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_PCRS].set) /* pcrs */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_pcrs (state[OPTION_PCRS].arg,
|
|
+ grub_tpm2_protector_ctx.pcrs,
|
|
+ &grub_tpm2_protector_ctx.pcr_count);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_BANK].set) /* bank */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_bank (state[OPTION_BANK].arg,
|
|
+ &grub_tpm2_protector_ctx.bank);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_TPM2KEY].set) /* tpm2key */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_file (state[OPTION_TPM2KEY].arg,
|
|
+ &grub_tpm2_protector_ctx.tpm2key);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_KEYFILE].set) /* keyfile */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_file (state[OPTION_KEYFILE].arg,
|
|
+ &grub_tpm2_protector_ctx.keyfile);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_SRK].set) /* srk */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_SRK].arg,
|
|
+ &grub_tpm2_protector_ctx.srk);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_ASYMMETRIC].set) /* asymmetric */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_asymmetric (state[OPTION_ASYMMETRIC].arg,
|
|
+ &grub_tpm2_protector_ctx.srk_type);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_NVINDEX].set) /* nvindex */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_NVINDEX].arg,
|
|
+ &grub_tpm2_protector_ctx.nv);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ err = grub_tpm2_protector_check_args (&grub_tpm2_protector_ctx);
|
|
+
|
|
+ /* This command only initializes the protector, so nothing else to do. */
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_clear_cmd_handler (grub_extcmd_context_t ctxt __attribute__ ((unused)),
|
|
+ int argc,
|
|
+ char **args __attribute__ ((unused)))
|
|
+{
|
|
+ if (argc)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("tpm2_key_protector_clear accepts no arguments"));
|
|
+
|
|
+ grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
|
|
+ grub_memset (&grub_tpm2_protector_ctx, 0, sizeof (grub_tpm2_protector_ctx));
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static struct grub_key_protector grub_tpm2_key_protector =
|
|
+ {
|
|
+ .name = "tpm2",
|
|
+ .recover_key = grub_tpm2_protector_recover_key
|
|
+ };
|
|
+
|
|
+GRUB_MOD_INIT (tpm2)
|
|
+{
|
|
+ grub_tpm2_protector_init_cmd =
|
|
+ grub_register_extcmd ("tpm2_key_protector_init",
|
|
+ grub_tpm2_protector_init_cmd_handler, 0,
|
|
+ N_("[-m mode] "
|
|
+ "[-p pcr_list] "
|
|
+ "[-b pcr_bank] "
|
|
+ "[-T tpm2_key_file_path] "
|
|
+ "[-k sealed_key_file_path] "
|
|
+ "[-s srk_handle] "
|
|
+ "[-a asymmetric_key_type] "
|
|
+ "[-n nv_index]"),
|
|
+ N_("Initialize the TPM2 key protector."),
|
|
+ grub_tpm2_protector_init_cmd_options);
|
|
+ grub_tpm2_protector_clear_cmd =
|
|
+ grub_register_extcmd ("tpm2_key_protector_clear",
|
|
+ grub_tpm2_protector_clear_cmd_handler, 0, NULL,
|
|
+ N_("Clear the TPM2 key protector if previously initialized."),
|
|
+ NULL);
|
|
+ grub_key_protector_register (&grub_tpm2_key_protector);
|
|
+}
|
|
+
|
|
+GRUB_MOD_FINI (tpm2)
|
|
+{
|
|
+ grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
|
|
+ grub_memset (&grub_tpm2_protector_ctx, 0, sizeof (grub_tpm2_protector_ctx));
|
|
+
|
|
+ grub_key_protector_unregister (&grub_tpm2_key_protector);
|
|
+ grub_unregister_extcmd (grub_tpm2_protector_clear_cmd);
|
|
+ grub_unregister_extcmd (grub_tpm2_protector_init_cmd);
|
|
+}
|
|
diff --git a/grub-core/tpm2/tpm2key.asn b/grub-core/tpm2/tpm2key.asn
|
|
new file mode 100644
|
|
index 000000000..7ad4b6a2a
|
|
--- /dev/null
|
|
+++ b/grub-core/tpm2/tpm2key.asn
|
|
@@ -0,0 +1,33 @@
|
|
+--
|
|
+-- TPM 2.0 key file format
|
|
+-- To generate tpm2key_asn1_tab.c: asn1Parser tpm2key.asn
|
|
+--
|
|
+TPM2KEY {}
|
|
+DEFINITIONS IMPLICIT TAGS ::=
|
|
+
|
|
+BEGIN
|
|
+
|
|
+TPMPolicy ::= SEQUENCE {
|
|
+ CommandCode [0] EXPLICIT INTEGER,
|
|
+ CommandPolicy [1] EXPLICIT OCTET STRING
|
|
+}
|
|
+
|
|
+TPMAuthPolicy ::= SEQUENCE {
|
|
+ Name [0] EXPLICIT UTF8String OPTIONAL,
|
|
+ Policy [1] EXPLICIT SEQUENCE OF TPMPolicy
|
|
+}
|
|
+
|
|
+TPMKey ::= SEQUENCE {
|
|
+ type OBJECT IDENTIFIER,
|
|
+ emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
|
|
+ policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
|
|
+ secret [2] EXPLICIT OCTET STRING OPTIONAL,
|
|
+ authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
|
|
+ description [4] EXPLICIT UTF8String OPTIONAL,
|
|
+ rsaParent [5] EXPLICIT BOOLEAN OPTIONAL,
|
|
+ parent INTEGER,
|
|
+ pubkey OCTET STRING,
|
|
+ privkey OCTET STRING
|
|
+}
|
|
+
|
|
+END
|
|
diff --git a/grub-core/tpm2/tpm2key.c b/grub-core/tpm2/tpm2key.c
|
|
new file mode 100644
|
|
index 000000000..7a55644e5
|
|
--- /dev/null
|
|
+++ b/grub-core/tpm2/tpm2key.c
|
|
@@ -0,0 +1,476 @@
|
|
+/*
|
|
+ * GRUB -- GRand Unified Bootloader
|
|
+ * Copyright (C) 2023 SUSE LLC
|
|
+ *
|
|
+ * GRUB is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * GRUB is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+#include <grub/libtasn1.h>
|
|
+#include <grub/list.h>
|
|
+#include <grub/misc.h>
|
|
+#include <grub/mm.h>
|
|
+#include <grub/tpm2/buffer.h>
|
|
+#include <grub/tpm2/tpm2key.h>
|
|
+
|
|
+extern asn1_static_node tpm2key_asn1_tab[];
|
|
+const char *sealed_key_oid = "2.23.133.10.1.5";
|
|
+
|
|
+static int
|
|
+asn1_allocate_and_read (asn1_node node, const char *name, void **content, grub_size_t *content_size)
|
|
+{
|
|
+ grub_uint8_t *tmpstr = NULL;
|
|
+ int tmpstr_size = 0;
|
|
+ int ret;
|
|
+
|
|
+ if (content == NULL)
|
|
+ return ASN1_MEM_ERROR;
|
|
+
|
|
+ ret = asn1_read_value (node, name, NULL, &tmpstr_size);
|
|
+ if (ret != ASN1_MEM_ERROR)
|
|
+ return ret;
|
|
+
|
|
+ tmpstr = grub_malloc (tmpstr_size);
|
|
+ if (tmpstr == NULL)
|
|
+ return ASN1_MEM_ERROR;
|
|
+
|
|
+ ret = asn1_read_value (node, name, tmpstr, &tmpstr_size);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return ret;
|
|
+
|
|
+ *content = tmpstr;
|
|
+ *content_size = tmpstr_size;
|
|
+
|
|
+ return ASN1_SUCCESS;
|
|
+}
|
|
+
|
|
+static int
|
|
+asn1_read_uint32 (asn1_node node, const char *name, grub_uint32_t *out)
|
|
+{
|
|
+ grub_uint32_t tmp = 0;
|
|
+ grub_uint8_t *ptr;
|
|
+ void *data = NULL;
|
|
+ grub_size_t data_size;
|
|
+ int ret;
|
|
+
|
|
+ ret = asn1_allocate_and_read (node, name, &data, &data_size);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return ret;
|
|
+
|
|
+ if (data_size > 4)
|
|
+ {
|
|
+ ret = ASN1_MEM_ERROR;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* convert the big-endian integer to host uint32 */
|
|
+ ptr = (grub_uint8_t *)&tmp + (4 - data_size);
|
|
+ grub_memcpy (ptr, data, data_size);
|
|
+ tmp = grub_be_to_cpu32 (tmp);
|
|
+
|
|
+ *out = tmp;
|
|
+error:
|
|
+ if (data)
|
|
+ grub_free (data);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t size)
|
|
+{
|
|
+ asn1_node tpm2key;
|
|
+ asn1_node tpm2key_asn1 = NULL;
|
|
+ void *type_oid = NULL;
|
|
+ grub_size_t type_oid_size = 0;
|
|
+ void *empty_auth = NULL;
|
|
+ grub_size_t empty_auth_size = 0;
|
|
+ int tmp_size = 0;
|
|
+ int ret;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /*
|
|
+ * TPMKey ::= SEQUENCE {
|
|
+ * type OBJECT IDENTIFIER,
|
|
+ * emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
|
|
+ * policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
|
|
+ * secret [2] EXPLICIT OCTET STRING OPTIONAL,
|
|
+ * authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
|
|
+ * description [4] EXPLICIT UTF8String OPTIONAL,
|
|
+ * rsaParent [5] EXPLICIT BOOLEAN OPTIONAL,
|
|
+ * parent INTEGER,
|
|
+ * pubkey OCTET STRING,
|
|
+ * privkey OCTET STRING
|
|
+ * }
|
|
+ */
|
|
+ ret = asn1_array2tree (tpm2key_asn1_tab, &tpm2key_asn1, NULL);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Failed to parse TPM2KEY ASN.1 array"));
|
|
+
|
|
+ ret = asn1_create_element (tpm2key_asn1, "TPM2KEY.TPMKey", &tpm2key);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Failed to create TPM2KEY.TPMKey"));
|
|
+
|
|
+ ret = asn1_der_decoding (&tpm2key, data, size, NULL);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Failed to decode TPM2KEY DER"));
|
|
+
|
|
+ /* Check if 'type' is Sealed Key or not */
|
|
+ ret = asn1_allocate_and_read (tpm2key, "type", &type_oid, &type_oid_size);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
|
|
+ N_("Not a valid TPM2KEY file"));
|
|
+
|
|
+ if (grub_memcmp (sealed_key_oid, type_oid, type_oid_size) != 0)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
|
|
+ N_("Not a valid TPM2KEY file"));
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* 'emptyAuth' must be 'TRUE' since we don't support password authorization */
|
|
+ ret = asn1_allocate_and_read (tpm2key, "emptyAuth", &empty_auth, &empty_auth_size);
|
|
+ if (ret != ASN1_SUCCESS || grub_strncmp ("TRUE", empty_auth, empty_auth_size) != 0)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("emptyAuth not TRUE"));
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* 'secret' should not be in a sealed key */
|
|
+ ret = asn1_read_value (tpm2key, "secret", NULL, &tmp_size);
|
|
+ if (ret != ASN1_ELEMENT_NOT_FOUND)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("\"secret\" not allowed for Sealed Key"));
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ *parsed_tpm2key = tpm2key;
|
|
+
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+error:
|
|
+ grub_free (type_oid);
|
|
+ grub_free (empty_auth);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+void
|
|
+grub_tpm2key_end_parsing (asn1_node tpm2key)
|
|
+{
|
|
+ asn1_delete_structure (&tpm2key);
|
|
+ tpm2key = NULL;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_rsaparent (asn1_node tpm2key, grub_uint8_t *rsaparent)
|
|
+{
|
|
+ void *bool_str = NULL;
|
|
+ grub_size_t bool_str_size = 0;
|
|
+ int ret;
|
|
+
|
|
+ if (rsaparent == NULL)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer detected"));
|
|
+
|
|
+ if (tpm2key == NULL)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, N_("Invalid parent node"));
|
|
+
|
|
+ ret = asn1_allocate_and_read (tpm2key, "rsaParent", &bool_str, &bool_str_size);
|
|
+ if (ret == ASN1_SUCCESS)
|
|
+ {
|
|
+ if (grub_strncmp ("TRUE", bool_str, bool_str_size) == 0)
|
|
+ *rsaparent = 1;
|
|
+ else
|
|
+ *rsaparent = 0;
|
|
+ }
|
|
+ else if (ret == ASN1_ELEMENT_NOT_FOUND)
|
|
+ *rsaparent = 0;
|
|
+ else
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, N_("Failed to retrieve rsaParent"));
|
|
+
|
|
+ grub_free (bool_str);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (parent == NULL)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer detected"));
|
|
+
|
|
+ if (tpm2key == NULL)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, N_("Invalid parent node"));
|
|
+
|
|
+ ret = asn1_read_uint32 (tpm2key, "parent", parent);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, N_("Failed to retrieve parent"));
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2key_get_octstring (asn1_node tpm2key, const char *name, void **data, grub_size_t *size)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (name == NULL || data == NULL || size == NULL)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter(s)"));
|
|
+
|
|
+ if (tpm2key == NULL)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, N_("Invalid %s node"), name);
|
|
+
|
|
+ ret = asn1_allocate_and_read (tpm2key, name, data, size);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR,
|
|
+ N_("Failed to retrieve %s"),
|
|
+ name);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size)
|
|
+{
|
|
+ return tpm2key_get_octstring (tpm2key, "pubkey", data, size);
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size)
|
|
+{
|
|
+ return tpm2key_get_octstring (tpm2key, "privkey", data, size);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * The string to fetch 'Policy' from 'authPolicy':
|
|
+ * authPolicy.?XX.Policy
|
|
+ */
|
|
+#define AUTHPOLICY_POL_MAX_STR "authPolicy.?XX.Policy"
|
|
+#define AUTHPOLICY_POL_MAX (sizeof (AUTHPOLICY_POL_MAX_STR))
|
|
+
|
|
+/*
|
|
+ * Expected strings for CommandCode and CommandPolicy:
|
|
+ * policy.?XX.CommandCode
|
|
+ * policy.?XX.CommandPolicy
|
|
+ * authPolicy.?XX.Policy.?YY.CommandCode
|
|
+ * authPolicy.?XX.Policy.?YY.CommandPolicy
|
|
+ */
|
|
+#define CMD_CODE_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandCode"
|
|
+#define CMD_POL_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandPolicy"
|
|
+#define CMD_CODE_MAX (sizeof (CMD_CODE_MAX_STR))
|
|
+#define CMD_POL_MAX (sizeof (CMD_POL_MAX_STR))
|
|
+
|
|
+static int
|
|
+tpm2key_get_policy_seq (asn1_node tpm2key, const char *prefix,
|
|
+ tpm2key_policy_t *policy_seq)
|
|
+{
|
|
+ tpm2key_policy_t tmp_seq = NULL;
|
|
+ tpm2key_policy_t policy = NULL;
|
|
+ int policy_n;
|
|
+ char cmd_code[CMD_CODE_MAX];
|
|
+ char cmd_pol[CMD_POL_MAX];
|
|
+ grub_size_t cmd_policy_len;
|
|
+ int i;
|
|
+ int ret;
|
|
+
|
|
+ ret = asn1_number_of_elements (tpm2key, prefix, &policy_n);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return ret;
|
|
+
|
|
+ /*
|
|
+ * Limit the number of policy commands to two digits (99)
|
|
+ * Although there is no upper bound for the number of policy commands,
|
|
+ * in practice, it takes one or two policy commands to unseal the key,
|
|
+ * so the 99 commands limit is more than enough.
|
|
+ */
|
|
+ if (policy_n > 100 || policy_n < 1)
|
|
+ return ASN1_VALUE_NOT_VALID;
|
|
+
|
|
+ /*
|
|
+ * Iterate the policy commands backwards since grub_list_push() prepends
|
|
+ * the item into the list.
|
|
+ */
|
|
+ for (i = policy_n; i >= 1; i--) {
|
|
+ policy = grub_zalloc (sizeof (struct tpm2key_policy));
|
|
+ if (policy == NULL)
|
|
+ {
|
|
+ ret = ASN1_MEM_ALLOC_ERROR;
|
|
+ goto error;
|
|
+ }
|
|
+ grub_snprintf (cmd_code, CMD_CODE_MAX, "%s.?%d.CommandCode", prefix, i);
|
|
+ grub_snprintf (cmd_pol, CMD_POL_MAX, "%s.?%d.CommandPolicy", prefix, i);
|
|
+
|
|
+ /* CommandCode [0] EXPLICIT INTEGER */
|
|
+ ret = asn1_read_uint32 (tpm2key, cmd_code, &policy->cmd_code);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return ret;
|
|
+
|
|
+ /* CommandPolicy [1] EXPLICIT OCTET STRING */
|
|
+ ret = tpm2key_get_octstring (tpm2key, cmd_pol, &policy->cmd_policy,
|
|
+ &cmd_policy_len);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ {
|
|
+ goto error;
|
|
+ }
|
|
+ else if (cmd_policy_len > GRUB_TPM2_BUFFER_CAPACITY)
|
|
+ {
|
|
+ /*
|
|
+ * CommandPolicy is the marshalled parameters for the TPM command so
|
|
+ * it should not be larger than the maximum TPM2 buffer.
|
|
+ */
|
|
+ ret = ASN1_VALUE_NOT_VALID;
|
|
+ goto error;
|
|
+ }
|
|
+ policy->cmd_policy_len = (grub_uint16_t)cmd_policy_len;
|
|
+
|
|
+ /* Prepend the policy command into the sequence */
|
|
+ grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (policy));
|
|
+ }
|
|
+
|
|
+ *policy_seq = tmp_seq;
|
|
+
|
|
+ return ASN1_SUCCESS;
|
|
+
|
|
+error:
|
|
+ if (policy)
|
|
+ {
|
|
+ grub_free (policy->cmd_policy);
|
|
+ grub_free (policy);
|
|
+ }
|
|
+ grub_tpm2key_free_policy_seq (tmp_seq);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = tpm2key_get_policy_seq (tpm2key, "policy", policy_seq);
|
|
+ if (ret == ASN1_ELEMENT_NOT_FOUND)
|
|
+ {
|
|
+ /* "policy" is optional, so it may not be available */
|
|
+ *policy_seq = NULL;
|
|
+ return GRUB_ERR_NONE;
|
|
+ }
|
|
+ else if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, N_("Failed to retrieve policy"));
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+void
|
|
+grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq)
|
|
+{
|
|
+ tpm2key_policy_t policy;
|
|
+ tpm2key_policy_t next;
|
|
+
|
|
+ if (policy_seq == NULL)
|
|
+ return;
|
|
+
|
|
+ FOR_LIST_ELEMENTS_SAFE (policy, next, policy_seq)
|
|
+ {
|
|
+ grub_free (policy->cmd_policy);
|
|
+ grub_free (policy);
|
|
+ }
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t *authpol_seq)
|
|
+{
|
|
+ tpm2key_authpolicy_t tmp_seq = NULL;
|
|
+ tpm2key_authpolicy_t authpol = NULL;
|
|
+ int authpol_n;
|
|
+ char authpol_pol[AUTHPOLICY_POL_MAX];
|
|
+ int i;
|
|
+ int ret;
|
|
+ grub_err_t err;
|
|
+
|
|
+ ret = asn1_number_of_elements (tpm2key, "authPolicy", &authpol_n);
|
|
+ if (ret == ASN1_ELEMENT_NOT_FOUND)
|
|
+ {
|
|
+ /* "authPolicy" is optional, so it may not be available */
|
|
+ *authpol_seq = NULL;
|
|
+ return GRUB_ERR_NONE;
|
|
+ }
|
|
+ else if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, N_("Failed to retrieve authPolicy"));
|
|
+
|
|
+ /* Limit the number of authPolicy elements to two digits (99) */
|
|
+ if (authpol_n > 100 || authpol_n < 1)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
+ N_("Invalid number of authPolicy elements"));
|
|
+
|
|
+ /*
|
|
+ * Iterate the authPolicy elements backwards since grub_list_push() prepends
|
|
+ * the item into the list.
|
|
+ */
|
|
+ for (i = authpol_n; i >= 1; i--) {
|
|
+ authpol = grub_zalloc (sizeof (struct tpm2key_authpolicy));
|
|
+ if (authpol == NULL)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
+ N_("Failed to allocate memory for authPolicy"));
|
|
+ goto error;
|
|
+ }
|
|
+ grub_snprintf (authpol_pol, AUTHPOLICY_POL_MAX, "authPolicy.?%d.Policy", i);
|
|
+
|
|
+ ret = tpm2key_get_policy_seq (tpm2key, authpol_pol, &authpol->policy_seq);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_READ_ERROR,
|
|
+ N_("Failed to retrieve policy from authPolicy"));
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Prepend the authPolicy element into the sequence */
|
|
+ grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (authpol));
|
|
+ }
|
|
+
|
|
+ *authpol_seq = tmp_seq;
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+
|
|
+error:
|
|
+ if (authpol)
|
|
+ {
|
|
+ grub_tpm2key_free_policy_seq (authpol->policy_seq);
|
|
+ grub_free (authpol);
|
|
+ }
|
|
+
|
|
+ grub_tpm2key_free_authpolicy_seq (tmp_seq);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+void
|
|
+grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq)
|
|
+{
|
|
+ tpm2key_authpolicy_t authpol;
|
|
+ tpm2key_authpolicy_t next;
|
|
+
|
|
+ if (authpol_seq == NULL)
|
|
+ return;
|
|
+
|
|
+ FOR_LIST_ELEMENTS_SAFE (authpol, next, authpol_seq)
|
|
+ {
|
|
+ grub_tpm2key_free_policy_seq (authpol->policy_seq);
|
|
+ grub_free (authpol);
|
|
+ }
|
|
+}
|
|
diff --git a/grub-core/tpm2/tpm2key_asn1_tab.c b/grub-core/tpm2/tpm2key_asn1_tab.c
|
|
new file mode 100644
|
|
index 000000000..6868924f9
|
|
--- /dev/null
|
|
+++ b/grub-core/tpm2/tpm2key_asn1_tab.c
|
|
@@ -0,0 +1,45 @@
|
|
+/*
|
|
+ * This file is generated by 'asn1Parser tpm2key.asn' and the '#include'
|
|
+ * headers are replaced with the ones in grub2.
|
|
+ * - 'grub/mm.h' for the definition of 'NULL'
|
|
+ * - 'grub/libtasn1.h' for the definition of 'asn1_static_node'
|
|
+ */
|
|
+
|
|
+#include <grub/mm.h>
|
|
+#include <grub/libtasn1.h>
|
|
+
|
|
+const asn1_static_node tpm2key_asn1_tab[] = {
|
|
+ { "TPM2KEY", 536875024, NULL },
|
|
+ { NULL, 1073741836, NULL },
|
|
+ { "TPMPolicy", 1610612741, NULL },
|
|
+ { "CommandCode", 1610620931, NULL },
|
|
+ { NULL, 2056, "0"},
|
|
+ { "CommandPolicy", 536879111, NULL },
|
|
+ { NULL, 2056, "1"},
|
|
+ { "TPMAuthPolicy", 1610612741, NULL },
|
|
+ { "Name", 1610637346, NULL },
|
|
+ { NULL, 2056, "0"},
|
|
+ { "Policy", 536879115, NULL },
|
|
+ { NULL, 1073743880, "1"},
|
|
+ { NULL, 2, "TPMPolicy"},
|
|
+ { "TPMKey", 536870917, NULL },
|
|
+ { "type", 1073741836, NULL },
|
|
+ { "emptyAuth", 1610637316, NULL },
|
|
+ { NULL, 2056, "0"},
|
|
+ { "policy", 1610637323, NULL },
|
|
+ { NULL, 1073743880, "1"},
|
|
+ { NULL, 2, "TPMPolicy"},
|
|
+ { "secret", 1610637319, NULL },
|
|
+ { NULL, 2056, "2"},
|
|
+ { "authPolicy", 1610637323, NULL },
|
|
+ { NULL, 1073743880, "3"},
|
|
+ { NULL, 2, "TPMAuthPolicy"},
|
|
+ { "description", 1610637346, NULL },
|
|
+ { NULL, 2056, "4"},
|
|
+ { "rsaParent", 1610637316, NULL },
|
|
+ { NULL, 2056, "5"},
|
|
+ { "parent", 1073741827, NULL },
|
|
+ { "pubkey", 1073741831, NULL },
|
|
+ { "privkey", 7, NULL },
|
|
+ { NULL, 0, NULL }
|
|
+};
|
|
diff --git a/include/grub/tpm2/internal/args.h b/include/grub/tpm2/internal/args.h
|
|
new file mode 100644
|
|
index 000000000..9f4c0eb9f
|
|
--- /dev/null
|
|
+++ b/include/grub/tpm2/internal/args.h
|
|
@@ -0,0 +1,49 @@
|
|
+/*
|
|
+ * GRUB -- GRand Unified Bootloader
|
|
+ * Copyright (C) 2022 Microsoft Corporation
|
|
+ *
|
|
+ * GRUB is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * GRUB is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+#ifndef GRUB_TPM2_INTERNAL_ARGS_HEADER
|
|
+#define GRUB_TPM2_INTERNAL_ARGS_HEADER 1
|
|
+
|
|
+#include <grub/err.h>
|
|
+#include <grub/tpm2/tpm2.h>
|
|
+
|
|
+struct grub_srk_type
|
|
+{
|
|
+ TPMI_ALG_PUBLIC type;
|
|
+ union {
|
|
+ TPM_KEY_BITS rsa_bits;
|
|
+ TPM_ECC_CURVE ecc_curve;
|
|
+ } detail;
|
|
+};
|
|
+typedef struct grub_srk_type grub_srk_type_t;
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs,
|
|
+ grub_uint8_t *pcr_count);
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2_protector_parse_asymmetric (const char *value,
|
|
+ grub_srk_type_t *srk_type);
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID *bank);
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle);
|
|
+
|
|
+#endif /* ! GRUB_TPM2_INTERNAL_ARGS_HEADER */
|
|
diff --git a/include/grub/tpm2/tpm2key.h b/include/grub/tpm2/tpm2key.h
|
|
new file mode 100644
|
|
index 000000000..c27b5305e
|
|
--- /dev/null
|
|
+++ b/include/grub/tpm2/tpm2key.h
|
|
@@ -0,0 +1,86 @@
|
|
+/*
|
|
+ * GRUB -- GRand Unified Bootloader
|
|
+ * Copyright (C) 2023 SUSE LLC
|
|
+ *
|
|
+ * GRUB is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * GRUB is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+#ifndef GRUB_TPM2_TPM2KEY_HEADER
|
|
+#define GRUB_TPM2_TPM2KEY_HEADER 1
|
|
+
|
|
+#include <grub/types.h>
|
|
+#include <grub/libtasn1.h>
|
|
+
|
|
+/*
|
|
+ * TPMPolicy ::= SEQUENCE {
|
|
+ * CommandCode [0] EXPLICIT INTEGER,
|
|
+ * CommandPolicy [1] EXPLICIT OCTET STRING
|
|
+ * }
|
|
+ */
|
|
+struct tpm2key_policy {
|
|
+ struct tpm2key_policy *next;
|
|
+ struct tpm2key_policy **prev;
|
|
+ grub_uint32_t cmd_code;
|
|
+ void *cmd_policy;
|
|
+ grub_uint16_t cmd_policy_len;
|
|
+};
|
|
+typedef struct tpm2key_policy *tpm2key_policy_t;
|
|
+
|
|
+/*
|
|
+ * TPMAuthPolicy ::= SEQUENCE {
|
|
+ * Name [0] EXPLICIT UTF8String OPTIONAL,
|
|
+ * Policy [1] EXPLICIT SEQUENCE OF TPMPolicy
|
|
+ * }
|
|
+ *
|
|
+ * Name is not a necessary part to unseal the key. Ignore it.
|
|
+ */
|
|
+struct tpm2key_authpolicy {
|
|
+ struct tpm2key_authpolicy *next;
|
|
+ struct tpm2key_authpolicy **prev;
|
|
+ /* char *name; */
|
|
+ tpm2key_policy_t policy_seq;
|
|
+};
|
|
+typedef struct tpm2key_authpolicy *tpm2key_authpolicy_t;
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t size);
|
|
+
|
|
+void
|
|
+grub_tpm2key_end_parsing (asn1_node tpm2key);
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_rsaparent (asn1_node tpm2key, grub_uint8_t *rsaparent);
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent);
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size);
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size);
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq);
|
|
+
|
|
+void
|
|
+grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq);
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t *authpol_seq);
|
|
+
|
|
+void
|
|
+grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq);
|
|
+
|
|
+#endif /* GRUB_TPM2_TPM2KEY_HEADER */
|
|
--
|
|
2.35.3
|
|
|