grub2/tpm-record-pcrs.patch

236 lines
5.6 KiB
Diff

--- a/grub-core/commands/tpm.c
+++ b/grub-core/commands/tpm.c
@@ -26,6 +26,9 @@
#include <grub/term.h>
#include <grub/verify.h>
#include <grub/dl.h>
+#include <grub/extcmd.h>
+#include <grub/tpm2/tpm2.h>
+#include <grub/efi/efi.h>
GRUB_MOD_LICENSE ("GPLv3+");
@@ -94,8 +97,214 @@
.verify_string = grub_tpm_verify_string,
};
+/*
+ * Preserve current PCR values and record them to an EFI variable
+ */
+#define GRUB2_PCR_BITMASK_DEFAULT ((1 << 16) - 1)
+#define GRUB2_PCR_BITMASK_ALL ((1 << 24) - 1)
+
+static const struct grub_arg_option grub_tpm_record_pcrs_options[] =
+ {
+ {
+ .longarg = "efivar",
+ .shortarg = 'E',
+ .flags = 0,
+ .arg = NULL,
+ .type = ARG_TYPE_STRING,
+ .doc =
+ N_("The EFI variable to publish the PCRs to (default GrubPcrSnapshot)"),
+ },
+
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static grub_err_t
+grub_tpm_parse_pcr_index (const char *word, const char **end_ret, unsigned int *index)
+{
+ const char *end;
+
+ if (!grub_isdigit (word[0]))
+ return GRUB_ERR_BAD_NUMBER;
+
+ *index = grub_strtoul(word, &end, 0);
+ if (*index > 32)
+ return GRUB_ERR_BAD_NUMBER;
+
+ *end_ret = end;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm_parse_pcr_list (const char *arg, grub_uint32_t *bitmask)
+{
+ const char *word, *end;
+ unsigned int index, last_index = 0;
+
+ if (!grub_strcmp (arg, "all"))
+ {
+ *bitmask = GRUB2_PCR_BITMASK_ALL;
+ return GRUB_ERR_NONE;
+ }
+
+ word = arg;
+ while (1)
+ {
+ if (grub_tpm_parse_pcr_index (word, &end, &index))
+ goto bad_pcr_index;
+
+ if (*end == '-')
+ {
+ if (grub_tpm_parse_pcr_index (end + 1, &end, &last_index) || last_index < index)
+ goto bad_pcr_index;
+
+ while (index <= last_index)
+ *bitmask |= (1 << (index++));
+ }
+ else
+ *bitmask |= (1 << index);
+
+ if (*end == '\0')
+ break;
+
+ if (*end != ',')
+ goto bad_pcr_index;
+
+ word = end + 1;
+ }
+
+ return GRUB_ERR_NONE;
+
+bad_pcr_index:
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("cannot parse PCR list \"%s\""), arg);
+}
+
+static inline unsigned int
+nbits(grub_uint32_t mask)
+{
+ unsigned int r = 0;
+
+ for (; mask != 0; mask >>= 1)
+ r += (mask & 1);
+ return r;
+}
+
+static grub_err_t
+grub_tpm_snapshot_pcrs (grub_uint32_t pcr_bitmask, const char *algo,
+ void **buffer_ret, grub_size_t *size_ret)
+{
+ char *buffer;
+ grub_size_t size = 65536;
+ unsigned int wpos = 0;
+ grub_uint8_t pcr;
+
+ buffer = grub_malloc (size);
+ for (pcr = 0; pcr < 32; ++pcr)
+ {
+ struct grub_tpm_digest *d;
+ unsigned int need, k;
+
+ if (!(pcr_bitmask & (1 << pcr)))
+ continue;
+
+ d = grub_tpm_read_pcr (pcr, algo);
+ if (d == NULL)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, N_("unable to read PCR %d from TPM"), pcr);
+ continue;
+ }
+
+ /* We need room for the PCR index, 2 spaces, newline, NUL. 16 should be enough. */
+ need = 16 + grub_strlen(d->algorithm) + 2 * d->size;
+ if (wpos + need > size)
+ {
+ buffer = grub_realloc (buffer, size + need);
+ if (buffer == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("Not enough memory when dumping PCR registers"));
+ }
+
+ grub_snprintf (buffer + wpos, size - wpos, "%02d %s ", pcr, d->algorithm);
+ wpos = grub_strlen(buffer);
+
+ for (k = 0; k < d->size; ++k)
+ {
+ grub_snprintf (buffer + wpos, size - wpos, "%02x", d->value[k]);
+ wpos += 2;
+ }
+
+ buffer[wpos++] = '\n';
+ buffer[wpos] = '\0';
+
+ grub_tpm_digest_free (d);
+ }
+
+ *buffer_ret = buffer;
+ *size_ret = wpos;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm_write_pcrs_to_efi (void *data, grub_size_t size, const char *var_name)
+{
+ grub_guid_t vendor_guid = { 0x7ce323f2, 0xb841, 0x4d30, { 0xa0, 0xe9, 0x54, 0x74, 0xa7, 0x6c, 0x9a, 0x3f }};
+ grub_err_t rc;
+
+ rc = grub_efi_set_variable_with_attributes(var_name, &vendor_guid,
+ data, size,
+ GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | GRUB_EFI_VARIABLE_RUNTIME_ACCESS);
+
+ if (rc)
+ return grub_error (GRUB_ERR_BAD_DEVICE, N_("Failed to publish PCR snapshot to UEFI variable %s"), var_name);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm_record_pcrs (grub_extcmd_context_t ctxt, int argc, char **args)
+{
+ struct grub_arg_list *state = ctxt->state;
+ grub_uint32_t pcr_bitmask = 0;
+ const char *efivar;
+ void *buffer = NULL;
+ grub_size_t size = 0;
+ int n, rv = 1;
+
+ if (argc == 0)
+ pcr_bitmask = GRUB2_PCR_BITMASK_DEFAULT;
+ else
+ {
+ for (n = 0; n < argc; ++n)
+ if (grub_tpm_parse_pcr_list (args[n], &pcr_bitmask))
+ return 1;
+ }
+
+ if (grub_tpm_snapshot_pcrs (pcr_bitmask, NULL, &buffer, &size))
+ goto out;
+
+ if (state[0].set)
+ efivar = state[0].arg;
+ else
+ efivar = "GrubPcrSnapshot";
+
+ if (grub_tpm_write_pcrs_to_efi (buffer, size, efivar))
+ goto out;
+
+ rv = 0;
+
+out:
+ if (buffer)
+ grub_free (buffer);
+ return rv;
+}
+
+static grub_extcmd_t cmd;
+
GRUB_MOD_INIT (tpm)
{
+ cmd = grub_register_extcmd ("tpm_record_pcrs", grub_tpm_record_pcrs, 0,
+ N_("LIST_OF_PCRS"),
+ N_("Snapshot one or more PCR values and record them in an EFI variable."),
+ grub_tpm_record_pcrs_options);
/*
* Even though this now calls ibmvtpm's grub_tpm_present() from GRUB_MOD_INIT(),
* it does seem to call it late enough in the initialization sequence so
@@ -109,6 +318,7 @@
GRUB_MOD_FINI (tpm)
{
+ grub_unregister_extcmd (cmd);
if (!grub_tpm_present())
return;
grub_verifier_unregister (&grub_tpm_verifier);