Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user facing message when SBAT self check fails #671

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/pe.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ EFI_STATUS verify_image(void *data, unsigned int datasize,
PE_COFF_LOADER_IMAGE_CONTEXT *context);

EFI_STATUS
verify_sbat_section(char *SBATBase, size_t SBATSize);
verify_sbat_section(char *SBATBase, size_t SBATSize, UINT16 *sbat_gen_expected, UINT16 *sbat_gen_found, CHAR8 **sbat_component_name);

EFI_STATUS
get_section_vma (UINTN section_num,
Expand Down
8 changes: 5 additions & 3 deletions include/sbat.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,14 @@ parse_sbat_section(char *section_base, size_t section_size, size_t *n,
struct sbat_section_entry ***entriesp);
void cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries);

EFI_STATUS verify_sbat(size_t n, struct sbat_section_entry **entries);
EFI_STATUS verify_sbat(size_t n, struct sbat_section_entry **entries, UINT16 *sbat_gen_expected, UINT16 *sbat_gen_found, CHAR8 **sbat_component_name);

#ifdef SHIM_UNIT_TEST
EFI_STATUS parse_sbat_var_data(list_t *entries, UINT8 *data, UINTN datasize);
EFI_STATUS verify_sbat_helper(list_t *sbat_var, size_t n,
struct sbat_section_entry **entries);
EFI_STATUS verify_sbat_helper(list_t *local_sbat_var, size_t n,
struct sbat_section_entry **entries,
UINT16 *sbat_gen_expected, UINT16 *sbat_gen_found,
CHAR8 **sbat_component_name);
#endif /* !SHIM_UNIT_TEST */
#endif /* !SBAT_H_ */
// vim:fenc=utf-8:tw=75:noet
4 changes: 2 additions & 2 deletions pe.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ generate_hash(char *data, unsigned int datasize,
}

EFI_STATUS
verify_sbat_section(char *SBATBase, size_t SBATSize)
verify_sbat_section(char *SBATBase, size_t SBATSize, UINT16 *sbat_gen_expected, UINT16 *sbat_gen_found, CHAR8 **sbat_component_name)
{
unsigned int i;
EFI_STATUS efi_status;
Expand Down Expand Up @@ -385,7 +385,7 @@ verify_sbat_section(char *SBATBase, size_t SBATSize)
entries[i]->vendor_url);
}

efi_status = verify_sbat(n, entries);
efi_status = verify_sbat(n, entries, sbat_gen_expected, sbat_gen_found, sbat_component_name);

cleanup_sbat_section_entries(n, entries);

Expand Down
41 changes: 28 additions & 13 deletions sbat.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,8 @@ cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries)
}

EFI_STATUS
verify_single_entry(struct sbat_section_entry *entry, struct sbat_var_entry *sbat_var_entry, bool *found)
verify_single_entry(struct sbat_section_entry *entry, struct sbat_var_entry *sbat_var_entry, bool *found, UINT16 *sbat_gen, UINT16 *sbat_var_gen)
{
UINT16 sbat_gen, sbat_var_gen;

if (strcmp((const char *)entry->component_name, (const char *)sbat_var_entry->component_name) == 0) {
dprint(L"component %a has a matching SBAT variable entry, verifying\n",
entry->component_name);
Expand All @@ -144,12 +142,12 @@ verify_single_entry(struct sbat_section_entry *entry, struct sbat_var_entry *sba
* atoi returns zero for failed conversion, so essentially
* badly parsed component_generation will be treated as zero
*/
sbat_gen = atoi((const char *)entry->component_generation);
sbat_var_gen = atoi((const char *)sbat_var_entry->component_generation);
*sbat_gen = atoi((const char *)entry->component_generation);
*sbat_var_gen = atoi((const char *)sbat_var_entry->component_generation);

if (sbat_gen < sbat_var_gen) {
if (*sbat_gen < *sbat_var_gen) {
dprint(L"component %a, generation %d, was revoked by %s variable\n",
entry->component_name, sbat_gen, SBAT_VAR_NAME);
entry->component_name, *sbat_gen, SBAT_VAR_NAME);
LogError(L"image did not pass SBAT verification\n");
return EFI_SECURITY_VIOLATION;
}
Expand Down Expand Up @@ -177,9 +175,10 @@ cleanup_sbat_var(list_t *entries)
}

EFI_STATUS
verify_sbat_helper(list_t *local_sbat_var, size_t n, struct sbat_section_entry **entries)
verify_sbat_helper(list_t *local_sbat_var, size_t n, struct sbat_section_entry **entries, UINT16 *sbat_gen_expected, UINT16 *sbat_gen_found, CHAR8 **sbat_component_name)
{
unsigned int i;
UINTN component_name_size;
list_t *pos = NULL;
EFI_STATUS efi_status = EFI_SUCCESS;
struct sbat_var_entry *sbat_var_entry;
Expand All @@ -192,26 +191,39 @@ verify_sbat_helper(list_t *local_sbat_var, size_t n, struct sbat_section_entry *
for (i = 0; i < n; i++) {
list_for_each(pos, local_sbat_var) {
bool found = false;
*sbat_gen_expected = 0;
*sbat_gen_found = 0;
sbat_var_entry = list_entry(pos, struct sbat_var_entry, list);
efi_status = verify_single_entry(entries[i], sbat_var_entry, &found);
if (EFI_ERROR(efi_status))
efi_status = verify_single_entry(entries[i], sbat_var_entry, &found, sbat_gen_found, sbat_gen_expected);
if (EFI_ERROR(efi_status)) {
if (found && efi_status == EFI_SECURITY_VIOLATION) {
component_name_size = strlen((const char *)entries[i]->component_name);
*sbat_component_name = AllocatePool(component_name_size + 1);
if (!*sbat_component_name) {
efi_status = EFI_OUT_OF_RESOURCES;
goto err;
}
memcpy(*sbat_component_name, (const char *)entries[i]->component_name, component_name_size + 1);
}
goto out;
}
if (found)
break;
}
}

out:
dprint(L"finished verifying SBAT data: %r\n", efi_status);
err:
return efi_status;
}

EFI_STATUS
verify_sbat(size_t n, struct sbat_section_entry **entries)
verify_sbat(size_t n, struct sbat_section_entry **entries, UINT16 *sbat_gen_expected, UINT16 *sbat_gen_found, CHAR8 **sbat_component_name)
{
EFI_STATUS efi_status;

efi_status = verify_sbat_helper(&sbat_var, n, entries);
efi_status = verify_sbat_helper(&sbat_var, n, entries, sbat_gen_expected, sbat_gen_found, sbat_component_name);
return efi_status;
}

Expand Down Expand Up @@ -559,9 +571,12 @@ set_sbat_uefi_variable(char *sbat_var_automatic, char *sbat_var_latest)
}

#ifndef SHIM_UNIT_TEST
UINT16 sbat_gen_expected = 0;
UINT16 sbat_gen_found = 0;
CHAR8 *sbat_component_name = NULL;
char *sbat_start = (char *)&_sbat;
char *sbat_end = (char *)&_esbat;
efi_status = verify_sbat_section(sbat_start, sbat_end - sbat_start - 1);
efi_status = verify_sbat_section(sbat_start, sbat_end - sbat_start - 1, &sbat_gen_expected, &sbat_gen_found, &sbat_component_name);
if (EFI_ERROR(efi_status)) {
CHAR16 *title = L"New SbatLevel would self-revoke current shim. Not applied";
CHAR16 *message = L"Press any key to continue";
Expand Down
37 changes: 34 additions & 3 deletions shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,10 @@ verify_buffer_sbat (char *data, int datasize,
{
int i;
EFI_IMAGE_SECTION_HEADER *Section;
EFI_STATUS efi_status;
UINT16 sbat_gen_expected = 0;
UINT16 sbat_gen_found = 0;
CHAR8 *sbat_component_name = NULL;
char *SBATBase = NULL;
size_t SBATSize = 0;

Expand Down Expand Up @@ -759,7 +763,11 @@ verify_buffer_sbat (char *data, int datasize,
}
}

return verify_sbat_section(SBATBase, SBATSize);
efi_status = verify_sbat_section(SBATBase, SBATSize, &sbat_gen_expected, &sbat_gen_found, &sbat_component_name);
if (sbat_component_name) {
FreePool(sbat_component_name);
}
return efi_status;
}

/*
Expand Down Expand Up @@ -1841,6 +1849,9 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
{
EFI_STATUS efi_status;
EFI_HANDLE image_handle;
UINT16 sbat_gen_expected = 0;
UINT16 sbat_gen_found = 0;
CHAR8 *sbat_component_name = NULL;

verification_method = VERIFIED_BY_NOTHING;

Expand Down Expand Up @@ -1927,7 +1938,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
goto die;
}

efi_status = verify_sbat_section(sbat_start, sbat_end - sbat_start - 1);
efi_status = verify_sbat_section(sbat_start, sbat_end - sbat_start - 1, &sbat_gen_expected, &sbat_gen_found, &sbat_component_name);
if (EFI_ERROR(efi_status)) {
perror(L"Verifiying shim SBAT data failed: %r\n",
efi_status);
Expand Down Expand Up @@ -1966,14 +1977,34 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
die:
console_print(L"Something has gone seriously wrong: %s: %r\n",
msgs[msg], efi_status);

/*
* Provide additional information when the SBAT self check fails
*/
if ( msg == SBAT_SELF_CHECK ) {
if (sbat_gen_expected == 0 && sbat_gen_found == 0 && sbat_component_name == NULL) {
// In this case something related to the .sbat section is wrong.
console_print(L"\n\nSomething went wrong validating SBAT. Either:\n"
"- The shim has no .sbat section or is corrupted\n"
"- Something went wrong internally");
} else {
console_print(L"\n\nYour shim has been revoked via SBAT. "
"Please update to a newer shim version.\n"
"For component \"%a\" expected at least level: %u. Current shim has: %u.\n",
sbat_component_name, sbat_gen_expected, sbat_gen_found);
}
}
#if defined(ENABLE_SHIM_DEVEL)
devel_egress(COLD_RESET);
#else
usleep(5000000);
usleep(90000000); // 90 sec
RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION,
0, NULL);
#endif
}
if (sbat_component_name) {
FreePool(sbat_component_name);
}

/*
* This variable is supposed to be set by second stages, so ensure it is
Expand Down
23 changes: 21 additions & 2 deletions test-sbat.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,10 +468,19 @@ test_verify_sbat_null_sbat_section(void)
status = parse_sbat_var_data(&test_sbat_var, sbat_var_data, sizeof(sbat_var_data));
assert_equal_goto(status, EFI_SUCCESS, err, "got %#x expected %#x\n");

status = verify_sbat_helper(&test_sbat_var, n, entries);
UINT16 sbat_gen_expected = 0;
UINT16 sbat_gen_found = 0;
CHAR8 *sbat_component_name = NULL;
status = verify_sbat_helper(&test_sbat_var, n, entries, &sbat_gen_expected, &sbat_gen_found, &sbat_component_name);
assert_equal_goto(sbat_component_name, NULL, err, "got %#x expected %#x\n");
assert_equal_goto(sbat_gen_expected, 0, err, "got %#x expected %#x\n");
assert_equal_goto(sbat_gen_found, 0, err, "got %#x expected %#x\n");
assert_equal_goto(status, EFI_SUCCESS, err, "got %#x expected %#x\n");
rc = 0;
err:
if (sbat_component_name) {
free(sbat_component_name);
}
cleanup_sbat_var(&test_sbat_var);

return rc;
Expand Down Expand Up @@ -973,12 +982,22 @@ test_parse_and_verify(void)
if (status != EFI_SUCCESS || list_empty(&sbat_var))
return -1;

status = verify_sbat(n_section_entries, section_entries);
UINT16 sbat_gen_expected = 0;
UINT16 sbat_gen_found = 0;
CHAR8 *sbat_component_name = NULL;
status = verify_sbat(n_section_entries, section_entries, &sbat_gen_expected, &sbat_gen_found, &sbat_component_name);
assert_nonzero_goto(sbat_component_name, err, "not expected component name to be NULL");
assert_goto(strcmp(sbat_component_name, "test1") == 0, err, "expected 'test1' got '%s'", sbat_component_name);
assert_equal_goto(sbat_gen_expected, 5, err, "expected %#x got %#x\n");
assert_equal_goto(sbat_gen_found, 1, err, "expected %#x got %#x\n");
assert_equal_goto(status, EFI_SECURITY_VIOLATION, err, "expected %#x got %#x\n");

rc = 0;
err:
cleanup_sbat_section_entries(n_section_entries, section_entries);
if (sbat_component_name) {
free(sbat_component_name);
}
cleanup_sbat_var(&sbat_var);

return rc;
Expand Down