where the UUID corresponds to /dev/sdb1.
Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
---
.gitignore | 1 +
Makefile.util.def | 16 +
configure.ac | 1 +
util/grub-protect.c | 1344 +++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1362 insertions(+)
create mode 100644 util/grub-protect.c
diff --git a/.gitignore b/.gitignore
index f6a1bd0..852327d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,6 +167,7 @@ widthspec.bin
/grub-ofpathname.exe
/grub-probe
/grub-probe.exe
+/grub-protect
/grub-reboot
/grub-render-label
/grub-render-label.exe
diff --git a/Makefile.util.def b/Makefile.util.def
index 39b53b3..05f9f09 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -52,6 +52,9 @@ library = {
common = grub-core/partmap/msdos.c;
common = grub-core/fs/proc.c;
common = grub-core/fs/archelp.c;
+ common = grub-core/tpm2/buffer.c;
+ common = grub-core/tpm2/mu.c;
+ common = grub-core/tpm2/tpm2.c;
};
library = {
@@ -207,6 +210,19 @@ program = {
};
program = {
+ name = grub-protect;
+ common = grub-core/osdep/init.c;
+ common = util/grub-protect.c;
+ common = util/probe.c;
+
+ ldadd = libgrubmods.a;
+ ldadd = libgrubgcry.a;
+ ldadd = libgrubkern.a;
+ ldadd = grub-core/lib/gnulib/libgnu.a;
+ ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR)
$(LIBGEOM)';
+};
+
+program = {
name = grub-mkrelpath;
mansection = 1;
diff --git a/configure.ac b/configure.ac
index 4f649ed..3a4dc5a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,6 +71,7 @@ grub_TRANSFORM([grub-mkpasswd-pbkdf2])
grub_TRANSFORM([grub-mkrelpath])
grub_TRANSFORM([grub-mkrescue])
grub_TRANSFORM([grub-probe])
+grub_TRANSFORM([grub-protect])
grub_TRANSFORM([grub-reboot])
grub_TRANSFORM([grub-script-check])
grub_TRANSFORM([grub-set-default])
diff --git a/util/grub-protect.c b/util/grub-protect.c
new file mode 100644
index 0000000..4e4bbd7
--- /dev/null
+++ b/util/grub-protect.c
@@ -0,0 +1,1344 @@
+/*
+ * 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 <config.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grub/crypto.h>
+#include <grub/emu/getroot.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/misc.h>
+#include <grub/tpm2/buffer.h>
+#include <grub/tpm2/mu.h>
+#include <grub/tpm2/tcg2.h>
+#include <grub/tpm2/tpm2.h>
+#include <grub/util/misc.h>
+
+#include "progname.h"
+
+typedef enum grub_protect_arg
+{
+ GRUB_PROTECT_ARG_ACTION = 1 << 0,
+ GRUB_PROTECT_ARG_PROTECTOR = 1 << 1,
+ GRUB_PROTECT_ARG_TPM2_DEVICE = 1 << 2,
+ GRUB_PROTECT_ARG_TPM2_PCRS = 1 << 3,
+ GRUB_PROTECT_ARG_TPM2_ASYMMETRIC = 1 << 4,
+ GRUB_PROTECT_ARG_TPM2_BANK = 1 << 5,
+ GRUB_PROTECT_ARG_TPM2_SRK = 1 << 6,
+ GRUB_PROTECT_ARG_TPM2_KEYFILE = 1 << 7,
+ GRUB_PROTECT_ARG_TPM2_OUTFILE = 1 << 8,
+ GRUB_PROTECT_ARG_TPM2_PERSIST = 1 << 9,
+ GRUB_PROTECT_ARG_TPM2_EVICT = 1 << 10
+} grub_protect_arg_t;
+
+typedef enum grub_protect_protector
+{
+ GRUB_PROTECT_TYPE_ERROR,
+ GRUB_PROTECT_TYPE_TPM2
+} grub_protect_protector_t;
+
+typedef enum grub_protect_action
+{
+ GRUB_PROTECT_ACTION_ERROR,
+ GRUB_PROTECT_ACTION_ADD,
+ GRUB_PROTECT_ACTION_REMOVE
+} grub_protect_action_t;
+
+struct grub_protect_args
+{
+ grub_protect_arg_t args;
+ grub_protect_action_t action;
+ grub_protect_protector_t protector;
+
+ const char *tpm2_device;
+ grub_uint8_t tpm2_pcrs[TPM_MAX_PCRS];
+ grub_uint8_t tpm2_pcr_count;
+ TPM_ALG_ID tpm2_asymmetric;
+ TPM_ALG_ID tpm2_bank;
+ TPM_HANDLE tpm2_srk;
+ const char *tpm2_keyfile;
+ const char *tpm2_outfile;
+ int tpm2_persist;
+ int tpm2_evict;
+};
+
+static int tpm2_fd = -1;
+
+static grub_err_t
+grub_protect_read_file (const char *filepath, void **buffer,
+ size_t *buffer_size)
+{
+ grub_err_t err;
+ FILE *f;
+ long len;
+ void *buf;
+
+ f = fopen (filepath, "rb");
+ if (!f)
+ return GRUB_ERR_FILE_NOT_FOUND;
+
+ if (fseek (f, 0, SEEK_END))
+ {
+ err = GRUB_ERR_FILE_READ_ERROR;
+ goto exit1;
+ }
+
+ len = ftell (f);
+ if (!len)
+ {
+ err = GRUB_ERR_FILE_READ_ERROR;
+ goto exit1;
+ }
+
+ rewind (f);
+
+ buf = grub_malloc (len);
+ if (!buf)
+ {
+ err = GRUB_ERR_OUT_OF_MEMORY;
+ goto exit1;
+ }
+
+ if (fread (buf, len, 1, f) != 1)
+ {
+ err = GRUB_ERR_FILE_READ_ERROR;
+ goto exit2;
+ }
+
+ *buffer = buf;
+ *buffer_size = len;
+
+ buf = NULL;
+ err = GRUB_ERR_NONE;
+
+exit2:
+ grub_free (buf);
+
+exit1:
+ fclose (f);
+
+ return err;
+}
+
+static grub_err_t
+grub_protect_write_file (const char *filepath, void *buffer, size_t
buffer_size)
+{
+ grub_err_t err;
+ FILE *f;
+
+ f = fopen (filepath, "wb");
+ if (!f)
+ return GRUB_ERR_FILE_NOT_FOUND;
+
+ if (fwrite (buffer, buffer_size, 1, f) != 1)
+ {
+ err = GRUB_ERR_WRITE_ERROR;
+ goto exit1;
+ }
+
+ err = GRUB_ERR_NONE;
+
+exit1:
+ fclose (f);
+
+ return err;
+}
+
+static grub_err_t
+grub_protect_get_grub_drive_for_file (const char *filepath, char **drive)
+{
+ grub_err_t err = GRUB_ERR_IO;
+ char *disk;
+ char **devices;
+ char *grub_dev;
+ char *grub_path;
+ char *efi_drive;
+ char *partition;
+ char *grub_drive;
+ grub_device_t dev;
+ grub_size_t grub_drive_len;
+ int n;
+
+ grub_path = grub_canonicalize_file_name (filepath);
+ if (!grub_path)
+ goto exit1;
+
+ devices = grub_guess_root_devices (grub_path);
+ if (!devices || !devices[0])
+ goto exit2;
+
+ disk = devices[0];
+
+ grub_util_pull_device (disk);
+
+ grub_dev = grub_util_get_grub_dev (disk);
+ if (!grub_dev)
+ goto exit3;
+
+ dev = grub_device_open (grub_dev);
+ if (!dev)
+ goto exit4;
+
+ efi_drive = grub_util_guess_efi_drive (disk);
+ if (!efi_drive)
+ goto exit5;
+
+ partition = grub_partition_get_name (dev->disk->partition);
+ if (!partition)
+ goto exit6;
+
+ grub_drive_len = grub_strlen (efi_drive) + grub_strlen (partition) + 3;
+ grub_drive = grub_malloc (grub_drive_len + 1);
+ if (!grub_drive)
+ goto exit7;
+
+ n = grub_snprintf (grub_drive, grub_drive_len + 1, "(%s,%s)", efi_drive,
+ partition);
+ if (n != grub_drive_len)
+ goto exit8;
+
+ *drive = grub_drive;
+ grub_drive = NULL;
+ err = GRUB_ERR_NONE;
+
+exit8:
+ grub_free (grub_drive);
+
+exit7:
+ grub_free (partition);
+
+exit6:
+ grub_free (efi_drive);
+
+exit5:
+ grub_device_close (dev);
+
+exit4:
+ grub_free (grub_dev);
+
+exit3:
+ grub_free (devices);
+
+exit2:
+ grub_free (grub_path);
+
+exit1:
+ return err;
+}
+
+grub_err_t
+grub_tcg2_get_max_output_size (grub_size_t *size)
+{
+ if (!size)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ *size = GRUB_TPM2_BUFFER_CAPACITY;
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_tcg2_submit_command (grub_size_t input_size, grub_uint8_t *input,
+ grub_size_t output_size, grub_uint8_t *output)
+{
+ static const grub_size_t header_size = sizeof (grub_uint16_t) +
+ (2 * sizeof(grub_uint32_t));
+
+ if (write (tpm2_fd, input, input_size) != input_size)
+ return GRUB_ERR_BAD_DEVICE;
+
+ if (read (tpm2_fd, output, output_size) < header_size)
+ return GRUB_ERR_BAD_DEVICE;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_open_device (const char *dev_node)
+{
+ if (tpm2_fd != -1)
+ return GRUB_ERR_NONE;
+
+ tpm2_fd = open (dev_node, O_RDWR);
+ if (tpm2_fd == -1)
+ {
+ fprintf (stderr, _("Could not open TPM device (Error: %u).\n"), errno);
+ return GRUB_ERR_FILE_NOT_FOUND;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_close_device (void)
+{
+ int err;
+
+ if (tpm2_fd == -1)
+ return GRUB_ERR_NONE;
+
+ err = close (tpm2_fd);
+ if (err)
+ {
+ fprintf (stderr, _("Could not close TPM device (Error: %u).\n"), errno);
+ return GRUB_ERR_IO;
+ }
+
+ tpm2_fd = -1;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_get_policy_digest (struct grub_protect_args *args,
+ TPM2B_DIGEST *digest)
+{
+ TPM_RC rc;
+ TPML_PCR_SELECTION pcr_sel = {
+ .count = 1,
+ .pcrSelections = {
+ {
+ .hash = args->tpm2_bank,
+ .sizeOfSelect = 3,
+ .pcrSelect = { 0 }
+ },
+ }
+ };
+ TPML_PCR_SELECTION pcr_sel_out = { 0 };
+ TPML_DIGEST pcr_values = { 0 };
+ grub_uint8_t *pcr_digest;
+ grub_size_t pcr_digest_len;
+ grub_uint8_t *pcr_concat;
+ grub_size_t pcr_concat_len;
+ grub_uint8_t *pcr_cursor;
+ const gcry_md_spec_t *hash_spec;
+ TPM2B_NONCE nonce = { 0 };
+ TPM2B_ENCRYPTED_SECRET salt = { 0 };
+ TPMT_SYM_DEF symmetric = { 0 };
+ TPMI_SH_AUTH_SESSION session = 0;
+ TPM2B_DIGEST pcr_digest_in = {
+ .size = TPM_SHA256_DIGEST_SIZE,
+ .buffer = { 0 }
+ };
+ TPM2B_DIGEST policy_digest = { 0 };
+ grub_uint8_t i;
+ grub_err_t err;
+
+ /* PCR Read */
+ for (i = 0; i < args->tpm2_pcr_count; i++)
+ pcr_sel
+ .pcrSelections[0]
+ .pcrSelect[TPM2_PCR_TO_SELECT(args->tpm2_pcrs[i])]
+ |= TPM2_PCR_TO_BIT(args->tpm2_pcrs[i]);
+
+ rc = TPM2_PCR_Read (NULL, &pcr_sel, NULL, &pcr_sel_out, &pcr_values, NULL);
+ if (rc != TPM_RC_SUCCESS)
+ {
+ fprintf (stderr, _("Failed to read PCRs (TPM error: 0x%x).\n"), rc);
+ return GRUB_ERR_BAD_DEVICE;
+ }
+
+ if ((pcr_sel_out.count != pcr_sel.count) ||
+ (pcr_sel.pcrSelections[0].sizeOfSelect !=
+ pcr_sel_out.pcrSelections[0].sizeOfSelect))
+ {
+ fprintf (stderr, _("Could not read all the specified PCRs.\n"));
+ return GRUB_ERR_BAD_DEVICE;
+ }
+
+ /* Compute PCR Digest */
+ switch (args->tpm2_bank)
+ {
+ case TPM_ALG_SHA1:
+ pcr_digest_len = TPM_SHA1_DIGEST_SIZE;
+ hash_spec = GRUB_MD_SHA1;
+ break;
+ case TPM_ALG_SHA256:
+ pcr_digest_len = TPM_SHA256_DIGEST_SIZE;
+ hash_spec = GRUB_MD_SHA256;
+ break;
+ default:
+ return GRUB_ERR_BAD_ARGUMENT;
+ }
+
+ pcr_digest = grub_malloc (pcr_digest_len);
+ if (!pcr_digest)
+ {
+ fprintf (stderr, _("Failed to allocate PCR digest buffer.\n"));
+ return GRUB_ERR_OUT_OF_MEMORY;
+ }
+
+ pcr_concat_len = pcr_digest_len * args->tpm2_pcr_count;
+ pcr_concat = grub_malloc (pcr_concat_len);
+ if (!pcr_concat)
+ {
+ err = GRUB_ERR_OUT_OF_MEMORY;
+ fprintf (stderr, _("Failed to allocate PCR concatenation buffer.\n"));
+ goto exit1;
+ }
+
+ pcr_cursor = pcr_concat;
+ for (i = 0; i < args->tpm2_pcr_count; i++)
+ {
+ if (pcr_values.digests[i].size != pcr_digest_len)
+ {
+ fprintf (stderr,
+ _("Bad PCR value size: expected %lu bytes but got %u
bytes.\n"),
+ pcr_digest_len, pcr_values.digests[i].size);
+ goto exit2;
+ }
+
+ grub_memcpy (pcr_cursor, pcr_values.digests[i].buffer, pcr_digest_len);
+ pcr_cursor += pcr_digest_len;
+ }
+
+ grub_crypto_hash (hash_spec, pcr_digest, pcr_concat, pcr_concat_len);
+
+ /* Start Trial Session */
+ nonce.size = TPM_SHA256_DIGEST_SIZE;
+ symmetric.algorithm = TPM_ALG_NULL;
+
+ rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, 0, &nonce, &salt,
+ TPM_SE_TRIAL, &symmetric, TPM_ALG_SHA256,
+ &session, NULL, 0);
+ if (rc != TPM_RC_SUCCESS)
+ {
+ fprintf (stderr,
+ _("Failed to start trial policy session (TPM error: 0x%x).\n"),
+ rc);
+ err = GRUB_ERR_BAD_DEVICE;
+ goto exit2;
+ }
+
+ /* PCR Policy */
+ memcpy (pcr_digest_in.buffer, pcr_digest, TPM_SHA256_DIGEST_SIZE);
+
+ rc = TPM2_PolicyPCR (session, NULL, &pcr_digest_in, &pcr_sel, NULL);
+ if (rc != TPM_RC_SUCCESS)
+ {
+ fprintf (stderr, _("Failed to submit PCR policy (TPM error: 0x%x).\n"),
+ rc);
+ err = GRUB_ERR_BAD_DEVICE;
+ goto exit3;
+ }
+
+ /* Retrieve Policy Digest */
+ rc = TPM2_PolicyGetDigest (session, NULL, &policy_digest, NULL);
+ if (rc != TPM_RC_SUCCESS)
+ {
+ fprintf (stderr, _("Failed to get policy digest (TPM error: 0x%x).\n"),
+ rc);
+ err = GRUB_ERR_BAD_DEVICE;
+ goto exit3;
+ }
+
+ /* Epilogue */
+ *digest = policy_digest;
+ err = GRUB_ERR_NONE;
+
+exit3:
+ TPM2_FlushContext (session);
+
+exit2:
+ grub_free (pcr_concat);
+
+exit1:
+ grub_free (pcr_digest);
+
+ return err;
+}
+
+static grub_err_t
+grub_protect_tpm2_get_srk (struct grub_protect_args *args, TPM_HANDLE *srk)
+{
+ TPM_RC rc;
+ TPM2B_PUBLIC public;
+ 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 srkHandle;
+
+ /* Find SRK */
+ rc = TPM2_ReadPublic (args->tpm2_srk, NULL, &public);
+ if (rc == TPM_RC_SUCCESS)
+ {
+ if (args->tpm2_persist)
+ fprintf (stderr,
+ _("Warning: --tpm2-persist was specified but the SRK already
exists on the TPM. Continuing anyway...\n"));
+
+ *srk = TPM2_SRK_HANDLE;
+ return GRUB_ERR_NONE;
+ }
+
+ /* The handle exists but its public area could not be read. */
+ if ((rc & ~TPM_RC_N_MASK) != TPM_RC_HANDLE)
+ {
+ fprintf (stderr,
+ _("The SRK exists on the TPM but its public area cannot be read (TPM
error: 0x%x).\n"),
+ rc);
+ return GRUB_ERR_BAD_DEVICE;
+ }
+
+ /* Create SRK */
+ authCommand.sessionHandle = TPM_RS_PW;
+ inPublic.publicArea.type = args->tpm2_asymmetric;
+ 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;
+
+ switch (args->tpm2_asymmetric)
+ {
+ case 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 = 2048;
+ inPublic.publicArea.parameters.rsaDetail.exponent = 0;
+ break;
+
+ case 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 = TPM_ECC_NIST_P256;
+ inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+ break;
+
+ default:
+ return GRUB_ERR_BAD_ARGUMENT;
+ }
+
+ rc = TPM2_CreatePrimary (TPM_RH_OWNER, &authCommand, &inSensitive, &inPublic,
+ &outsideInfo, &creationPcr, &srkHandle, &outPublic,
+ &creationData, &creationHash, &creationTicket,
+ &srkName, NULL);
+ if (rc != TPM_RC_SUCCESS)
+ {
+ fprintf (stderr, _("Failed to create SRK (TPM error: 0x%x).\n"), rc);
+ return GRUB_ERR_BAD_DEVICE;
+ }
+
+ /* Persist SRK */
+ if (args->tpm2_persist)
+ {
+ rc = TPM2_EvictControl (TPM_RH_OWNER, srkHandle, args->tpm2_srk,
+ &authCommand, NULL);
+ if (rc == TPM_RC_SUCCESS)
+ {
+ TPM2_FlushContext (srkHandle);
+ srkHandle = args->tpm2_srk;
+ }
+ else
+ fprintf (stderr,
+ _("Warning: Failed to persist SRK (TPM error: 0x%x\n). Continuing
anyway...\n"),
+ rc);
+ }
+
+ /* Epilogue */
+ *srk = srkHandle;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_seal (TPM2B_DIGEST *policyDigest, TPM_HANDLE srk,
+ grub_uint8_t *clearText, grub_size_t clearTextLength,
+ TPM2_SEALED_KEY *sealed_key)
+{
+ TPM_RC rc;
+ TPMS_AUTH_COMMAND authCommand = { 0 };
+ TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
+ TPM2B_PUBLIC inPublic = { 0 };
+ TPM2B_DATA outsideInfo = { 0 };
+ TPML_PCR_SELECTION pcr_sel = { 0 };
+ TPM2B_PRIVATE outPrivate = { 0 };
+ TPM2B_PUBLIC outPublic = { 0 };
+
+ /* Seal Data */
+ authCommand.sessionHandle = TPM_RS_PW;
+
+ inSensitive.sensitive.data.size = clearTextLength;
+ memcpy(inSensitive.sensitive.data.buffer, clearText, clearTextLength);
+
+ inPublic.publicArea.type = TPM_ALG_KEYEDHASH;
+ inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
+ inPublic.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM_ALG_NULL;
+ inPublic.publicArea.authPolicy = *policyDigest;
+
+ rc = TPM2_Create (srk, &authCommand, &inSensitive, &inPublic, &outsideInfo,
+ &pcr_sel, &outPrivate, &outPublic, NULL, NULL, NULL, NULL);
+ if (rc != TPM_RC_SUCCESS)
+ {
+ fprintf (stderr, _("Failed to seal key (TPM error: 0x%x).\n"), rc);
+ return GRUB_ERR_BAD_DEVICE;
+ }
+
+ /* Epilogue */
+ sealed_key->public = outPublic;
+ sealed_key->private = outPrivate;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_export_sealed_key (const char *filepath,
+ TPM2_SEALED_KEY *sealed_key)
+{
+ grub_err_t err;
+ struct grub_tpm2_buffer buf;
+
+ grub_tpm2_buffer_init (&buf);
+ grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&buf, &sealed_key->public);
+ grub_tpm2_mu_TPM2B_Marshal (&buf, sealed_key->private.size,
+ sealed_key->private.buffer);
+ if (buf.error)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ err = grub_protect_write_file (filepath, buf.data, buf.size);
+ if (err)
+ fprintf (stderr, _("Could not write sealed key file (Error: %u).\n"),
+ errno);
+
+ return err;
+}
+
+static grub_err_t
+grub_protect_tpm2_add (struct grub_protect_args *args)
+{
+ grub_err_t err;
+ grub_uint8_t *key;
+ grub_size_t key_size;
+ TPM_HANDLE srk;
+ TPM2B_DIGEST policy_digest;
+ TPM2_SEALED_KEY sealed_key;
+ char *grub_drive = NULL;
+
+ grub_protect_get_grub_drive_for_file (args->tpm2_outfile, &grub_drive);
+
+ err = grub_protect_tpm2_open_device (args->tpm2_device);
+ if (err)
+ return err;
+
+ err = grub_protect_read_file (args->tpm2_keyfile, (void **)&key, &key_size);
+ if (err)
+ goto exit1;
+
+ if (key_size > TPM_MAX_SYM_DATA)
+ {
+ fprintf (stderr,
+ _("Input key is too long, maximum allowed size is %u bytes.\n"),
+ TPM_MAX_SYM_DATA);
+ return GRUB_ERR_OUT_OF_RANGE;
+ }
+
+ err = grub_protect_tpm2_get_srk (args, &srk);
+ if (err)
+ goto exit2;
+
+ err = grub_protect_tpm2_get_policy_digest (args, &policy_digest);
+ if (err)
+ goto exit3;
+
+ err = grub_protect_tpm2_seal (&policy_digest, srk, key, key_size,
+ &sealed_key);
+ if (err)
+ goto exit3;
+
+ err = grub_protect_tpm2_export_sealed_key (args->tpm2_outfile, &sealed_key);
+ if (err)
+ goto exit3;
+
+ if (grub_drive)
+ {
+ printf (_("GRUB drive for the sealed key file: %s\n"), grub_drive);
+ grub_free (grub_drive);
+ }
+ else
+ {
+ fprintf (stderr,
+ _("Warning: Could not determine GRUB drive for sealed key
file.\n"));
+ err = GRUB_ERR_NONE;
+ }
+
+exit3:
+ TPM2_FlushContext (srk);
+
+exit2:
+ grub_free (key);
+
+exit1:
+ grub_protect_tpm2_close_device ();
+
+ return err;
+}
+
+static grub_err_t
+grub_protect_tpm2_remove (struct grub_protect_args *args)
+{
+ TPM_RC rc;
+ TPM2B_PUBLIC public;
+ TPMS_AUTH_COMMAND authCommand = { 0 };
+ grub_err_t err;
+
+ if (!args->tpm2_evict)
+ {
+ printf (_("--tpm2-evict not specified, nothing to do.\n"));
+ return GRUB_ERR_NONE;
+ }
+
+ err = grub_protect_tpm2_open_device (args->tpm2_device);
+ if (err)
+ return err;
+
+ /* Find SRK */
+ rc = TPM2_ReadPublic (args->tpm2_srk, NULL, &public);
+ if (rc != TPM_RC_SUCCESS)
+ {
+ fprintf (stderr, _("SRK with handle 0x%x not found.\n"), args->tpm2_srk);
+ err = GRUB_ERR_BAD_ARGUMENT;
+ goto exit1;
+ }
+
+ /* Evict SRK */
+ authCommand.sessionHandle = TPM_RS_PW;
+
+ rc = TPM2_EvictControl (TPM_RH_OWNER, args->tpm2_srk, args->tpm2_srk,
+ &authCommand, NULL);
+ if (rc != TPM_RC_SUCCESS)
+ {
+ fprintf (stderr,
+ _("Failed to evict SRK with handle 0x%x (TPM Error: 0x%x).\n"),
+ args->tpm2_srk, rc);
+ err = GRUB_ERR_BAD_DEVICE;
+ goto exit2;
+ }
+
+ err = GRUB_ERR_NONE;
+
+exit2:
+ TPM2_FlushContext (args->tpm2_srk);
+
+exit1:
+ grub_protect_tpm2_close_device ();
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_run (struct grub_protect_args *args)
+{
+ switch (args->action)
+ {
+ case GRUB_PROTECT_ACTION_ADD:
+ return grub_protect_tpm2_add (args);
+
+ case GRUB_PROTECT_ACTION_REMOVE:
+ return grub_protect_tpm2_remove (args);
+
+ default:
+ return GRUB_ERR_BAD_ARGUMENT;
+ }
+}
+
+static grub_err_t
+grub_protect_tpm2_args_verify (struct grub_protect_args *args)
+{