SbatLevelRT for the next boot (bsc#1230316) OBS-URL: https://build.opensuse.org/package/show/Base:System/pcr-oracle?expand=0&rev=38
423 lines
13 KiB
Diff
423 lines
13 KiB
Diff
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, ¤t_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
|