pcr-oracle/fix-bsc1230316-predict-sbatlevelrt.patch

423 lines
13 KiB
Diff
Raw Permalink Normal View History

From 5f73c8d655465470498c91f238788d2ffa48de81 Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Tue, 25 Feb 2025 16:13:28 +0800
Subject: [PATCH 1/2] Locate .sbatlevel section in shim.efi
The .sbatlevel section in shim.efi will be used to set SbatLevelRT and
we need the section to predict SbatLevelRT.
Per PE format SPEC(*), the section name is
"An 8-byte, null-padded UTF-8 encoded string. If the string is exactly 8
8 characters long, there is no terminating null. For longer names, this
field contains a slash (/) that is followed by an ASCII representation
of a decimal number that is an offset into the string table."
Before looking for the .sbatlevel section, we have to get the string
table, To get the offset to the string table:
string_table_offset = symbol_table_offset +
(number_of_symbols * symbol_size)
With the offset, we can go further to look for the longer section names
and get the .sbatlevel section.
(*) https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
Signed-off-by: Gary Lin <glin@suse.com>
---
src/authenticode.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++
src/authenticode.h | 2 ++
2 files changed, 77 insertions(+)
Index: pcr-oracle-0.5.4/src/authenticode.c
===================================================================
--- pcr-oracle-0.5.4.orig/src/authenticode.c
+++ pcr-oracle-0.5.4/src/authenticode.c
@@ -91,10 +91,14 @@ struct pecoff_image_info {
uint16_t machine_id;
uint16_t num_sections;
uint32_t symtab_offset;
+ uint32_t num_symbols;
uint16_t optional_hdr_size;
uint32_t optional_hdr_offset;
uint32_t section_table_offset;
+
+ uint32_t strtab_offset;
+ uint32_t strtab_size;
} pe_hdr;
struct {
@@ -111,6 +115,9 @@ struct pecoff_image_info {
pecoff_section_t * section;
authenticode_image_info_t auth_info;
+
+ /* The contents of .sbatlevel */
+ buffer_t * sbatlevel;
};
#define MSDOS_STUB_PE_OFFSET 0x3c
@@ -147,6 +154,7 @@ void
pecoff_image_info_free(pecoff_image_info_t *img)
{
buffer_free(img->data);
+ buffer_free(img->sbatlevel);
free(img->display_name);
free(img->data_dirs);
free(img->section);
@@ -351,6 +359,8 @@ __pecoff_process_header(buffer_t *in, pe
if (!__pecoff_get_u32(in, img, PECOFF_HEADER_SYMTAB_POS_OFFSET, &img->pe_hdr.symtab_offset))
return false;
+ if (!__pecoff_get_u32(in, img, PECOFF_HEADER_SYMTAB_CNT_OFFSET, &img->pe_hdr.num_symbols))
+ return false;
img->pe_hdr.optional_hdr_offset = img->pe_hdr.offset + PECOFF_HEADER_LENGTH;
if (!__pecoff_get_u16(in, img, PECOFF_HEADER_OPTIONAL_HDR_SIZE_OFFSET, &img->pe_hdr.optional_hdr_size))
@@ -358,6 +368,19 @@ __pecoff_process_header(buffer_t *in, pe
img->pe_hdr.section_table_offset = img->pe_hdr.optional_hdr_offset + img->pe_hdr.optional_hdr_size;
+ /* String table follows symbol table immediately.
+ * One symbol is 18 bytes, so the offset to string table is
+ * symtab_offset + num_symbols * 18
+ *
+ * ref: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-string-table
+ * https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-symbol-table
+ */
+ if (img->pe_hdr.symtab_offset != 0) {
+ img->pe_hdr.strtab_offset = img->pe_hdr.symtab_offset + (img->pe_hdr.num_symbols * 18);
+ if (!__pecoff_get_u32(in, img, img->pe_hdr.strtab_offset, &img->pe_hdr.strtab_size))
+ return false;
+ }
+
return true;
}
@@ -451,6 +474,27 @@ __pecoff_process_optional_header(buffer_
}
static bool
+__pecoff_name_to_offset(char *sec_name, uint32_t *offset)
+{
+ uint32_t result = 0;
+ int i;
+
+ if (sec_name[0] != '/')
+ return false;
+
+ for (i = 1; i < 8 && sec_name[i] != '\0'; i++) {
+ if (sec_name[i] < '0' || sec_name[i] > '9')
+ return false;
+
+ result = result * 10 + sec_name[i] - '0';
+ }
+
+ *offset = result;
+
+ return true;
+}
+
+static bool
__pecoff_process_sections(buffer_t *in, pecoff_image_info_t *info)
{
unsigned int tbl_offset = info->pe_hdr.section_table_offset;
@@ -458,6 +502,9 @@ __pecoff_process_sections(buffer_t *in,
buffer_t hdr;
unsigned int i;
pecoff_section_t *sec;
+ uint32_t str_offset;
+ char *long_name;
+ buffer_t *sec_buf;
pe_debug(" Processing %u sections (table at offset %u)\n", num_sections, tbl_offset);
@@ -483,6 +530,27 @@ __pecoff_process_sections(buffer_t *in,
pe_debug(" Section %-8s raw %7u at 0x%08x-0x%08x\n",
sec->name, sec->raw.size, sec->raw.addr, sec->raw.addr + sec->raw.size);
+ /* Process the section names longer than 8 bytes */
+ long_name = NULL;
+ if (__pecoff_name_to_offset(sec->name, &str_offset) &&
+ str_offset < info->pe_hdr.strtab_size) {
+ long_name = (char *)(in->data + info->pe_hdr.strtab_offset + str_offset);
+ pe_debug(" Long Name: %s\n", long_name);
+ }
+
+ /* Get sbatlevel from .sbatlevel section */
+ if (long_name != NULL && strcmp(long_name, ".sbatlevel") == 0) {
+ if (!(sec_buf = buffer_alloc_write(sec->raw.size)))
+ return false;
+
+ if (!buffer_seek_read(in, sec->raw.addr))
+ return false;
+
+ if (!buffer_copy(in, sec->raw.size, sec_buf))
+ return false;
+
+ info->sbatlevel = sec_buf;
+ }
}
/* We are supposed to sort the sections in ascending order, but we're not doing it here, we
@@ -506,6 +574,7 @@ __pecoff_show_header(pecoff_image_info_t
pe_debug(" Architecture: %s\n", __pecoff_get_machine(img));
pe_debug(" Number of sections: %d\n", img->pe_hdr.num_sections);
pe_debug(" Symbol table position: 0x%08x\n", img->pe_hdr.symtab_offset);
+ pe_debug(" String table position: 0x%08x\n", img->pe_hdr.strtab_offset);
pe_debug(" Optional header size: %d\n", img->pe_hdr.optional_hdr_size);
}
@@ -751,3 +820,9 @@ authenticode_get_signer(const pecoff_ima
cert_table_free(cert_tbl);
return signer;
}
+
+buffer_t *
+pecoff_image_get_sbatlevel(pecoff_image_info_t *img)
+{
+ return img->sbatlevel;
+}
Index: pcr-oracle-0.5.4/src/authenticode.h
===================================================================
--- pcr-oracle-0.5.4.orig/src/authenticode.h
+++ pcr-oracle-0.5.4/src/authenticode.h
@@ -29,5 +29,7 @@ extern tpm_evdigest_t * authenticode_get
extern cert_table_t * authenticode_get_certificate_table(const pecoff_image_info_t *img);
extern parsed_cert_t * authenticode_get_signer(const pecoff_image_info_t *);
+extern buffer_t * pecoff_image_get_sbatlevel(pecoff_image_info_t *);
+
#endif /* AUTHENTICODE_H */
Index: pcr-oracle-0.5.4/src/efi-application.c
===================================================================
--- pcr-oracle-0.5.4.orig/src/efi-application.c
+++ pcr-oracle-0.5.4/src/efi-application.c
@@ -315,6 +315,7 @@ __tpm_event_efi_bsa_rehash(const tpm_eve
const struct efi_bsa_event *evspec = &parsed->efi_bsa_event;
const char *new_application;
struct efi_bsa_event evspec_clone;
+ buffer_t *sbatlevel;
/* Some BSA events do not refer to files, but to some data blobs residing somewhere on a device.
* We're not yet prepared to handle these, so we hope the user doesn't mess with them, and
@@ -348,6 +349,14 @@ __tpm_event_efi_bsa_rehash(const tpm_eve
}
}
+ /* Set the sbatlevel section from shim.efi */
+ if (ctx->sbatlevel == NULL
+ && (sbatlevel = pecoff_image_get_sbatlevel(evspec->img_info)) != NULL) {
+ if ((ctx->sbatlevel = buffer_alloc_write(sbatlevel->size)) == NULL
+ || !buffer_copy(sbatlevel, sbatlevel->size, ctx->sbatlevel))
+ return NULL;
+ }
+
if (ctx->use_pesign)
return __efi_application_rehash_pesign(ctx, evspec->efi_partition, evspec->efi_application);
Index: pcr-oracle-0.5.4/src/efi-variable.c
===================================================================
--- pcr-oracle-0.5.4.orig/src/efi-variable.c
+++ pcr-oracle-0.5.4/src/efi-variable.c
@@ -93,6 +93,159 @@ __tpm_event_efi_variable_build_event(con
return bp;
}
+#define SBATLEVELRT_VARNAME "SbatLevelRT-605dab50-e046-4300-abb6-3dd810dd8b23"
+#define SBATPOLICY_VARNAME "SbatPolicy-605dab50-e046-4300-abb6-3dd810dd8b23"
+#define SECUREBOOT_VARNAME "SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c"
+
+#define POLICY_LATEST 1
+#define POLICY_AUTOMATIC 2
+#define POLICY_RESET 3
+
+#define SBAT_ORIGINAL "sbat,1,2021030218\n"
+
+static bool
+parse_sbatlevel_section(buffer_t *sec, char **sbat_automatic, char **sbat_latest)
+{
+ uint32_t fmt_ver;
+ uint32_t offset_auto;
+ uint32_t offset_latest;
+
+ if (!buffer_get_u32le(sec, &fmt_ver)
+ || !buffer_get_u32le(sec, &offset_auto)
+ || !buffer_get_u32le(sec, &offset_latest))
+ return false;
+
+ if (offset_auto >= offset_latest)
+ return false;
+
+ if (!buffer_seek_read(sec, offset_auto + 4))
+ return false;
+ *sbat_automatic = (char *)buffer_read_pointer(sec);
+
+ if (!buffer_seek_read(sec, offset_latest + 4))
+ return false;
+ *sbat_latest = (char *)(buffer_read_pointer(sec));
+
+ return true;
+}
+
+static bool
+fetch_sbat_datestamp(const char *sbat, size_t size, uint32_t *datestamp)
+{
+ uint32_t date = 0;
+ size_t i;
+
+ /* Expected string: "sbat,X,YYYYYYYYYY\n" */
+ if (size < 17)
+ return false;
+
+ if (strncmp(sbat, "sbat,", 5) != 0)
+ return false;
+
+ for (i = 5; i < size && sbat[i] != ','; i++);
+ i++;
+ if (i >= size)
+ return false;
+
+ for (; i < size && sbat[i] != '\n'; i++) {
+ if (sbat[i] < '0' || sbat[i] > '9')
+ return false;
+ date = date * 10 + sbat[i] - '0';
+ }
+
+ *datestamp = date;
+ return true;
+}
+
+static buffer_t *
+efi_sbatlevel_get_record(buffer_t *sbatlevel)
+{
+ char *sbat_automatic;
+ char *sbat_latest;
+ const char *sbat_candidate;
+ const char *sbat_current;
+ buffer_t *buffer = NULL;
+ buffer_t *sbatlvlrt = NULL;
+ buffer_t *result = NULL;
+ uint8_t secureboot;
+ uint8_t sbatpolicy;
+ uint32_t current_date;
+ uint32_t candidate_date;
+ bool sbat_reset = false;
+
+ if (!parse_sbatlevel_section(sbatlevel, &sbat_automatic, &sbat_latest)) {
+ error("Unable to process SbatLevel\n");
+ return NULL;
+ }
+
+ buffer = runtime_read_efi_variable(SECUREBOOT_VARNAME);
+ if (buffer == NULL || !buffer_get_u8(buffer, &secureboot))
+ secureboot = 0;
+ buffer_free(buffer);
+
+ buffer = runtime_read_efi_variable(SBATPOLICY_VARNAME);
+ if (buffer == NULL || !buffer_get_u8(buffer, &sbatpolicy))
+ sbatpolicy = POLICY_AUTOMATIC;
+ buffer_free(buffer);
+
+ switch (sbatpolicy) {
+ case POLICY_LATEST:
+ sbat_candidate = sbat_latest;
+ break;
+ case POLICY_AUTOMATIC:
+ sbat_candidate = sbat_automatic;
+ break;
+ case POLICY_RESET:
+ if (secureboot == 1) {
+ infomsg("SBAT cannot be reset when Secure Boot is enabled.\n");
+ sbat_candidate = sbat_automatic;
+ } else {
+ sbat_candidate = SBAT_ORIGINAL;
+ }
+ break;
+ default:
+ error("Invalid SBAT policy\n");
+ return NULL;
+ }
+
+ if ((sbatlvlrt = runtime_read_efi_variable(SBATLEVELRT_VARNAME)) == NULL) {
+ error("Unable to read SbatLevelRT\n");
+ return NULL;
+ }
+
+ sbat_current = (const char *)buffer_read_pointer(sbatlvlrt);
+
+ if (!fetch_sbat_datestamp(sbat_current, sbatlvlrt->size, &current_date)
+ || !fetch_sbat_datestamp(sbat_candidate, strlen(sbat_candidate), &candidate_date)) {
+ error("Unable to get SBAT timestamp\n");
+ goto fail;
+ }
+
+ debug("Current SBAT datestampe: %u\n", current_date);
+ debug("Candidate SBAT datestampe: %u\n", candidate_date);
+
+ if (current_date >= candidate_date && sbat_reset == false) {
+ debug("Use current SbatLevel\n");
+ result = sbatlvlrt;
+ } else {
+ debug("Use candidate SbatLevel\n");
+ buffer_free(sbatlvlrt);
+
+ /* Copy the candidate SbatLevel string without the terminating null */
+ if ((result = buffer_alloc_write(strlen(sbat_candidate))) == NULL
+ || !buffer_put(result, sbat_candidate, strlen(sbat_candidate)))
+ goto fail;
+ }
+
+ return result;
+
+fail:
+ buffer_free(sbatlvlrt);
+ buffer_free(result);
+
+ return NULL;
+}
+
enum {
HASH_STRATEGY_EVENT,
HASH_STRATEGY_DATA,
@@ -114,6 +267,11 @@ efi_variable_authority_get_record(const
} else
if (!strcmp(var_short_name, "MokListRT")) {
db_name = "MokList";
+ } else
+ if (!strcmp(var_short_name, "SbatLevel")) {
+ if (ctx->sbatlevel != NULL)
+ return efi_sbatlevel_get_record(ctx->sbatlevel);
+ return runtime_read_efi_variable(var_name);
} else {
/* Read as-is (this could be SbatLevel, or some other variable that's not
* a signature db). */
Index: pcr-oracle-0.5.4/src/eventlog.c
===================================================================
--- pcr-oracle-0.5.4.orig/src/eventlog.c
+++ pcr-oracle-0.5.4/src/eventlog.c
@@ -1148,6 +1148,7 @@ tpm_event_log_rehash_ctx_init(tpm_event_
void
tpm_event_log_rehash_ctx_destroy(tpm_event_log_rehash_ctx_t *ctx)
{
+ buffer_free(ctx->sbatlevel);
}
void
Index: pcr-oracle-0.5.4/src/eventlog.h
===================================================================
--- pcr-oracle-0.5.4.orig/src/eventlog.h
+++ pcr-oracle-0.5.4/src/eventlog.h
@@ -209,6 +209,8 @@ typedef struct tpm_event_log_rehash_ctx
/* This get set when the user specifies --next-kernel */
char * boot_entry_path;
uapi_boot_entry_t * boot_entry;
+
+ buffer_t * sbatlevel;
} tpm_event_log_rehash_ctx_t;
#define GRUB_COMMAND_ARGV_MAX 32